WIN32汇编语言教程:第13章 进程控制 · 13.4 进程的隐藏(1)
进程隐藏技术用得最多的地方就是在病毒和木马中,因为这些不适合出现在阳光下的程序,越隐蔽生存率就越高。在当今Windows环境下,病毒和木马流传得越来越广泛,让读者适当了解这方面的技术可以在防治方面起到积极的作用,技术这种东西就是这样,大家都知道的“秘技”也就不再是“秘技”了,所以,大家都知道了进程隐藏是怎么一回事,进程隐藏起来也就不那么隐蔽了。
13.4.1 在Windows 9x中隐藏进程
在Windows 9x系列操作系统中,可以通过Kernel32.dll中的一个未公开函数来完成隐藏功能,这个函数就是RegisterServiceProcess,该函数的功能是将一个进程注册为系统服务进程,由于Windows的任务管理器并不列出系统服务进程,所以可以用它来隐藏进程,不过该函数在Windows NT系列中并不存在。
RegisterServiceProcess函数的使用方法是:
invoke RegisterServiceProcess,dwProcessID,dwFlag
dwProcessID指明目标进程的进程ID,参数dwFlag指定是注册还是撤销,指定TRUE的话,进程被注册为系统服务进程,如果指定为FALSE,则进程的属性恢复为普通进程属性。
Kernel32.lib导入库中并没有这个函数的导入信息,如果要使用这个函数,程序需要自己装入库文件并使用GetProcAddress函数获取入口地址后使用(方法请复习第11章)。所附光盘的Chapter13\HideProcess9x目录中的例子程序演示了该函数的使用方法。汇编源代码HideProcess9x.asm的内容如下:
.386
.model flat,stdcall
option casemap:none
;####################################################################
; Include 文件定义
;####################################################################
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
;####################################################################
; 数据段
;####################################################################
.const
szFunction db 'RegisterServiceProcess',0
szDllKernel db 'Kernel32.dll',0
szText db '现在请按下Ctrl+Alt+Del调出任务管理器查看是否存在本进程',0
szCaption db '在Windows 9x中隐藏进程',0
szErr db '本功能只在Windows 9x中提供',0
;####################################################################
; 代码段
;####################################################################
.code
start:
invoke GetModuleHandle,offset szDllKernel
.if eax
invoke GetProcAddress,eax,offset szFunction
.if eax
mov ebx,eax
push TRUE
invoke GetCurrentProcessId
push eax
call ebx
invoke MessageBox,NULL,offset szText,\
offset szCaption,MB_OK
jmp @F
.endif
.endif
invoke MessageBox,NULL,offset szErr,NULL,\
MB_OK or MB_ICONWARNING
@@:
invoke ExitProcess,NULL
;####################################################################
end start
由于可以确认Kernel32.dll库已经被装载到进程的地址空间中(GetProcAddress等函数就包括在这个库中,因此这个库肯定已经被装入),所以例子中使用GetModuleHandle函数而不是使用LoadLibrary函数来获取库句柄,这样就可以省去一个FreeLibrary的调用。接下来,程序使用GetProcAddress函数获取RegisterServiceProcess函数的入口地址,如果获取成功,则使用GetCurrentProcessId函数获取当前进程的ID并将这个ID注册为系统服务进程。
RegisterServiceProcess的缺点就是只能“欺骗”Windows的任务管理器,使用快照函数还是可以将全部进程枚举出来,即使是13.3.1节中的ProcessList.exe例子,也可以发现用RegisterServiceProcess隐藏的进程。所以使用这个函数只能实现简单的进程隐藏功能。相比之下,Windows NT下远程线程的功能就要强大得多了。
13.4.2 Windows NT中的远程线程
在Windows 9x中将进程注册为系统服务进程就能够从任务管理器中隐形,但在NT下就不同了。首先,NT下不存在RegisterServiceProcess函数;其次,NT的任务管理器会列出所有的进程(包括系统进程),即使一个进程将自己的可执行文件放在很隐蔽的目录中,文件名还是会被任务管理器列出来,所以想让别人看不见进程是不可能的。
当然,如果不用进程也能运行程序的话,那是最好不过的办法了,但是不用进程是无法执行文件的。
再从另一个角度考虑,如果进程显示的不是正确的名称呢,这也可以起到掩护作用,如果在DLL中执行我们的代码,系统报告的进程名称是装入DLL的进程的名称,而不是DLL本身的名称。
在Windows NT中还有另一种办法,那就是使用远程线程,使用它可以在其他进程中创建一个线程,由于线程是被所属进程拥有的,所以任务管理器中列出来的还是所属进程的名称。
1. Windows NT的远程操作函数
有两个函数可以用来实现上述功能:VirtualAllocEx和CreateRemoteThread。这两个函数都只能在Windows NT下使用。
VirtualAllocEx函数可以用来在其他进程的地址空间内申请内存,当然申请到的内存也是位于目标进程的地址空间内的,将这个函数和WriteProcessMemory函数配合就可以在目标进程的地址空间中“造”出任何东西来。
VirtualAllocEx函数的用法是:
invoke VirtualAllocEx,hProcess,lpAddress,dwSize,\
flAllocationType,flProtect
.if eax
mov lpMemory,eax
.endif
在10.1.5节中已经介绍过虚拟内存管理函数VirtualAlloc,VirtualAllocEx函数就是这个函数的扩充,相比之下,VirtualAllocEx函数多了一个参数hProcess,其他参数定义和使用的方法都和VirtualAlloc函数相同,读者可以回过头去查看这些参数的用法。新增的hProcess参数用来指定要申请内存的进程句柄,如果需要在目标进程中使用VirtualAllocEx函数,那么必须对进程拥有PROCESS_VM_OPERATION权限。
如果内存申请成功,函数返回一个指针,指向申请到的内存块,当然这个指针是针对目标进程的地址空间的。如果内存申请失败,函数返回NULL。
CreateRemoteThread函数用来在其他进程内创建一个线程,当然创建的线程是运行于目标进程的地址空间内的,它和目标进程自己创建的线程并没有什么区别。函数的用法是:
invoke CreateRemoteThread,hProcess,lpThreadAttributes,dwStackSize,\
lpStartAddress,lpParameter,dwCreationFlags,lpThreadId