WIN32汇编语言教程:第13章 进程控制 · 13.4 进程的隐藏(5)
.const
szErrOpen db '无法打开远程线程!',0
szDesktopClass db 'Progman',0
szDesktopWindow db 'Program Manager',0
szDllKernel db 'Kernel32.dll',0
szLoadLibrary db 'LoadLibraryA',0
szGetProcAddress db 'GetProcAddress',0
szGetModuleHandle db 'GetModuleHandleA',0
;####################################################################
.code
;####################################################################
include RemoteCode.asm
start:
invoke GetModuleHandle,addr szDllKernel
mov ebx,eax
invoke GetProcAddress,ebx,offset szLoadLibrary
mov lpLoadLibrary,eax
invoke GetProcAddress,ebx,offset szGetProcAddress
mov lpGetProcAddress,eax
invoke GetProcAddress,ebx,offset szGetModuleHandle
mov lpGetModuleHandle,eax
;********************************************************************
; 查找文件管理器窗口并获取进程ID,然后打开进程
;********************************************************************
invoke FindWindow,addr szDesktopClass,addr szDesktopWindow
invoke GetWindowThreadProcessId,eax,offset dwProcessID
mov dwThreadID,eax
invoke OpenProcess,PROCESS_CREATE_THREAD or PROCESS_VM_WRITE\
PROCESS_VM_OPERATION,FALSE,dwProcessID
.if eax
mov hProcess,eax
;********************************************************************
; 在进程中分配空间并将执行代码拷贝过去,然后创建一个远程线程
;********************************************************************
invoke VirtualAllocEx,hProcess,NULL,REMOTE_CODE_LENGTH,MEM_COMMIT,\
PAGE_EXECUTE_READWRITE
.if eax
mov lpRemoteCode,eax
invoke WriteProcessMemory,hProcess,lpRemoteCode,\
offset REMOTE_CODE_START,REMOTE_CODE_LENGTH,\
offset dwTemp
invoke WriteProcessMemory,hProcess,lpRemoteCode,\
offset lpLoadLibrary,sizeof dword * 3,offset dwTemp
mov eax,lpRemoteCode
add eax,offset _RemoteThread - offset REMOTE_CODE_START
invoke CreateRemoteThread,hProcess,NULL,0,eax,0,0,NULL
invoke CloseHandle,eax
.endif
invoke CloseHandle,hProcess
.else
invoke MessageBox,NULL,addr szErrOpen,NULL,MB_OK or MB_ICONWARNING
.endif
invoke ExitProcess,NULL
;####################################################################
end start
在程序开始,首先获取LoadLibrary,GetProcAddress和GetModuleHandle函数的入口地址,这些地址将在远程线程中被用来获取其他API的入口地址。
接下来就是打开Explorer.exe进程的操作,程序通过GetWindowThreadProcessId和OpenProcess函数来完成,函数中使用的窗口句柄是桌面的窗口句柄,因为桌面就是由文件管理器进程创建的,桌面的窗口类是“Progman”,窗口名称是“Program Manager”,使用FindWindow函数就可以很方便地找到它。在打开进程的时候必须包括对应的权限,PROCESS_CREATE_THREAD权限将允许创建远程线程,PROCESS_VM_OPERATION权限将允许在目标进程中分配内存并将远程代码写到里面。
程序使用VirtualAllocEx函数在目标进程中分配内存,在分配内存的时候,内存属性必须指定为PAGE_EXECUTE_READWRITE,这样分配到的内存可以有执行和读写的权限,分配方式必须指定为MEM_COMMIT,这样内存才会被提交到物理内存中去。
在分配到内存以后,程序使用WriteProcessMemory将远程代码写入,然后再一次将LoadLibrary,GetProcAddress和GetModuleHandle函数的地址写入到远程代码的数据段中。并不将这3个函数的地址存放到远程代码中一次性写入的原因在于:远程代码(包括远程代码使用的数据)是定义在本地的代码段中的,而本地的代码段是只读的,我们无法在本地对它们进行写入初始化数据的操作,所以只好采用远程写入的方式。
最后,用CreateRemoteThread函数创建远程线程后就万事大吉了。编译链接以后运行可执行文件可看到,窗口正常出现了,一眼看上去,这个窗口和别的窗口没有任何不同!但是在任务管理器中却没有多出任何新的进程。
假如远程线程不是这样“招摇过市”地创建了一个窗口,而是在后台偷偷地运行的话,大家能不能从各种蛛丝马迹来发现它的存在呢?反正笔者是找不到它的,因为它仅存在于目标进程的内存中,并不对应任何磁盘文件,当远程线程被执行的时候,惟一可以发现的就是Explorer.exe进程中的活动线程多了一个,使用的内存多了一点而已,但是活动线程用工具软件查看也只能看到一个线程ID,又怎么知道这个线程不是Explorer.exe进程自己的呢?
以上代码用在不合适的地方可能产生危害,笔者第一次公开这段代码,其目的就是希望能对有害代码的防治起到积极的作用,请读者负责任地使用这段代码。