远程线程指把当前进程部分代码注入到其他进程做为线程执行,虽然钩子程序能挂钩其他程序的消息,但钩子程序退出,注入的dll也就退出了,而远程线程不会随着本地进程退出而结束。而且可以处理更多的事情,而不局限于消息。由于98不支持所以只能在nt内核上运行,下面是制作远程线程需要使用的api。
获取进程句柄方法之一是使用GetWindowThreadProcessId函数,这个函数可以从一个窗口句柄获得创建窗口进程的id,而获得一个窗口句柄可以用FindWindow轻易得到。
HWND FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName);
lpClassName, // 窗口类名称,可以指定为NULL,光指定窗口名称即可
lpWindowName // 窗口名称。
如果两个参数都为0,则获得最顶层窗口的句柄。
DWORD GetWindowThreadProcessId(HWND hWnd,LPDWORD lpdwProcessId);
Hwnd 进程拥有的窗口句柄
LpdwProcessID 指向用来存放进程ID的变量
得到进程ID 之后,可以使用 OpenProcess函数来获得进程句柄
HANDLE OpenProcess(DWORD dwDesiredAccess,BOOL bInheritHandle,DWORD dwProcessId);
DwDesiredAccess,对打开的进程的访问权限,可以是下列值的组合:
PROCESS_ALL_ACCESS--------------------等于下面所有权限的组合
PROCESS_CREATE_THREAD-----------------允许创建远程线程
PROCESS_DUP_HANDLE--------------------允许进程句柄被复制
PROCESS_QUERY_INFORMATION-------------允许使用GetExitCodeProcess函数查询和GetProrityClass 查询进程信息
PROCESS_SET_INFORMATION---------------允许使用SetPriorityClass函数设置进程的优先级
PROCESS_TERMINATE----------------------允许结束进程
PROCESS_VM_OPERATION-------------------允许使用WriteProcessMemory函数或者VirtualProtectEx来修改进程的地址空间
PROCESS_VM_READ------------------------允许对读取进程地址空间
PROCESS_VM_WRITE-----------------------允许使用写入进程地址空间
BInheritHandle参数,指定返回的进程句柄是否可以被当前进程的子进程继承
DwProcessId 参数指定目标进程的进程ID
还有一种方法是使用CreateToolhelp32Snapshot(快照)函数来获得进程句柄,上面的方法进程必须要有窗口,而快照函数不需要进程拥有窗口,暂不介绍
下面是读写进程数据的两个api函数:
BOOL ReadProcessMemory(
HANDLE hProcess, // 进程句柄
LPCVOID lpBaseAddress, // 要读取的目标进程起始内存
LPVOID lpBuffer, // 本地进程用来存放读取内容的数据缓冲区
SIZE_T nSize, // 要从目标进程读取得数据长度
SIZE_T * lpNumberOfBytesRead // 要读出的到本地的数量,为NULL则忽略这个参数
BOOL WriteProcessMemory(
HANDLE hProcess, // handle to process
LPVOID lpBaseAddress, // base of memory area
LPVOID lpBuffer, // data buffer
SIZE_T nSize, // count of bytes to write
SIZE_T * lpNumberOfBytesWritten // count of bytes written
);
要注入远程线程,必须要在目标进程中开辟一段空间,来存放远程线程代码。
LPVOID VirtualAllocEx(
HANDLE hProcess, // 要开辟内存的进程
LPVOID lpAddress, // 从进程那个地址开始分配,为NULL,则系统决定
SIZE_T dwSize, // 要分配的空间大小
DWORD flAllocationType, // 分配的类型,一般用 MEM_COMMIT即可
DWORD flProtect // 这段内存访问的权限,PAGE_EXECUTE_READWRITE,远程线程所处空间必须可读可执行
);
下面是注入远程线程的需要使用的函数:
HANDLE CreateRemoteThread(
HANDLE hProcess, // 要写入远程线程进程句柄
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程安全属性
DWORD dwStackSize, // 初始化堆栈大小
LPTHREAD_START_ROUTINE lpStartAddress, // 远程线程函数
LPVOID lpParameter, // 远程线程参数
DWORD dwCreationFlags, // 标志,可以创建挂起的线程等等
LPDWORD lpThreadId // 用来返回线程ID的指针
);
代码重定位
有了这些函数就可以把一段代码插入到目标进程,这段代码将作为目标进程中一个独立的线程运行。但是代码编译时,全局变量、Api函数等等,将被编译为地址形式,这是地址对于本地进程是可读可执行的,对于目标进程,读取这些地址是非法的,windows这样做,可以保证每个进程都拥有自己独立的4GB空间,而不互相干扰(处于ring3的进程互相访问是非法的)。对于所有的高级语言,包括C语言,根本不能解决重定位问题,程序只能先写一个dll文件,然后用 CreateRemoteThread 把LoadLibrary 函数注入到目标进程中。LoadLibrary 函数调用dll 文件,执行自己想要的功能,不过这样用一些进程工具可以看到目标进程多了一个dll。重定位是汇编语言的拿手好戏。
Call @F
@@:
pop ebx
sub ebx,offset @B
现在 ebx 即得到了代码的实际地址和汇编地址之间的偏差,所以在需要重定位的代码上加上这个偏移值即可。
远程线程代码
远程线程小例子
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;sample write by ameng, http://ameng.tianyablog.com
;使用远程线程注入到explorer中,避免出现在win2k任务管理器中,并实现看护win2k进程,发现进程退出
;马上启动同样的另一个进程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 使用 nmake 或下列命令进行编译和链接:
; ml /c /coff RemoteThread.asm
; rc RemoteThread.rc
; Link /subsystem:windows RemoteThread.obj RemoteThread.res
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.386
.model flat,stdcall
option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
include macro.inc
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
KnlOpenProcessStr db 'OpenProcess',0
KnlWaitForObjectStr db 'WaitForSingleObject',0
KnlWinExecStrdb 'WinExec',0
KnlGetModuleHandleStrdb 'GetModuleHandleA',0
KnlGetProcAddressStrdb 'GetProcAddress',0
FileNamedb 'nodead.exe',0
szDllKerneldb 'Kernel32.dll',0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;下面两个变量是explorer.exe 进程窗口的类名(RegisterClassA参数中设定的)和进程标题名字
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
szDesktopClassdb 'Progman',0
szDesktopWindowdb 'Program Manager',0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data?
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
KnlOpenProcessdd?
KnlWaitForSingleObjectdd?
KnlWinExecdd?
KnlGetModuleHandledd?
KnlGetProcAddressdd?
dwProcessIDdd?
dwThreadIDdd?
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include RemoteCode.asm
Start:
invokeGetModuleHandle,addr szDllKernel
mov ebx,eax
invokeGetProcAddress,ebx,offset KnlOpenProcessStr
movKnlOpenProcess,eax
invokeGetProcAddress,ebx,offset KnlWaitForObjectStr
movKnlWaitForSingleObject,eax
invokeGetProcAddress,ebx,offset KnlWinExecStr
movKnlWinExec,eax
invokeGetProcAddress,ebx,offset KnlGetProcAddressStr
movKnlGetProcAddress,eax
invokeGetProcAddress,ebx,offset KnlGetModuleHandleStr
movKnlGetModuleHandle,eax
invoke FindWindow,addr szDesktopClass,addr szDesktopWindow
invoke GetWindowThreadProcessId,eax,offset dwProcessID
mov dwThreadID,eax
invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,dwProcessID
test eax,eax
jz OpenProcessError
mov ebx,eax
invoke VirtualAllocEx,ebx,NULL,REMOTE_CODE_LENGTH,MEM_COMMIT,PAGE_EXECUTE_READWRITE
or eax,eax
jz OpenProcessError
mov edi,eax
push eax
invoke WriteProcessMemory,ebx,edi,offset REMOTE_CODE_START,REMOTE_CODE_LENGTH,NULL
invoke WriteProcessMemory,ebx,edi,offset KnlOpenProcess,sizeof dword * 5,NULL
addedi,offset Protect2kProc - offset REMOTE_CODE_START
invoke CreateRemoteThread,ebx,NULL,0,edi,0,0,NULL
invoke CloseHandle,ebx
invoke Sleep,100h
invokeMessageBoxA,0,offset FileName,offset FileName,0
OpenProcessError:
invoke ExitProcess,0
end Start
macro.inc
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 将参数列表的顺序翻转
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
reverseArgsmacroarglist:VARARG
localtxt,count
txtTEXTEQU<>
count= 0
fori,
count= count + 1
txtTEXTEQU @CatStr(i,,<%txt>)
endm
ifcount GT 0
txtSUBSTR txt,1,@SizeStr(%txt)-1
endif
exitmtxt
endm
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 建立一个类似于 invoke 的 Macro
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_invokemacro_Proc,args:VARARG
localcount
count= 0
%fori,< reverseArgs( args ) >
count= count + 1
pushi
endm
calldword ptr _Proc
endm
RemoteThread.asm
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;注入远程进程执行的代码
;sample code write by ameng
;http://ameng.tianyablog.com
;equ this [:类型],则变量包含的段和偏移地址都和下一句相同,类型指变量类型
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
REMOTE_CODE_STARTequ this byte
_KnlOpenProcessdd?
_KnlWaitForSingleObject dd?
_KnlWinExecdd?
_KnlGetModuleHandledd?
_KnlGetProcAddressdd?
_KnlSleepdd?
_Error2db'overflow2',0;循环把这个字符串覆盖了?
_FileNamedb'c:\wap32.exe',0; 要看护的进程路径
_WinNamedb'Our First Dialog Box',0;要看护的进程窗口名字
_hInstancedd?
_KnlFindWindowdd?
_KnlMessageBoxdd?
_ErrorMsgdb'overflow',0 ;循环把这个字符串覆盖了?
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;要从User32 中提取使用的api函数名称
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_szDllUserdb'user32.dll',0
_szDllKerneldb'kernel32.dll',0
szFindWindowdb'FindWindowA',0
szMessageBoxdb'MessageBoxA',0,0 ;多一个0用于结束循环
szSleepdb'Sleep',0,0
Protect2kProc proc uses ebx edi esi
localhModuleUser
localhModuleKernel
call @F
@@:
pop ebx
sub ebx,offset @B
_invoke[ebx+ _KnlGetModuleHandle],NULL
testeax,eax
jzExitProtectProc
mov[ebx+ _hInstance],eax
leaeax,[ebx+ offset _szDllUser]
_invoke[ebx+_KnlGetModuleHandle],eax
movhModuleUser,eax
leaesi,[ebx+offset szFindWindow]
leaedi,[ebx+offset _KnlFindWindow]
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;从User32.dll中取api函数地址的循环
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.whileTRUE
_invoke[ebx+_KnlGetProcAddress],hModuleUser,esi
mov[edi],eax
addedi,4
@@:
lodsb
oral,al
jnz@B
.break .if ! byte ptr [esi+1]
.endw
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;从Kernel32.dll中取api函数地址的循环
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
leaeax,[ebx+ offset _szDllKernel]
_invoke[ebx+_KnlGetModuleHandle],eax
movhModuleKernel,eax
leaesi,[ebx+offset szSleep]
leaedi,[ebx+offset _KnlSleep]
.whileTRUE
_invoke[ebx+_KnlGetProcAddress],hModuleKernel,esi
mov[edi],eax
addedi,4
@@:
lodsb
oral,al
jnz@B
.break .if ! byte ptr [esi+1]
.endw
call_WinMain
ExitProtectProc:
ret
Protect2kProc endp
_WinMainproc uses ebx esi edi
call@F
@@:
popebx
subebx,@B
leaedi,[ebx+ offset _ErrorMsg]
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;看护循环,发现进程退出马上重启一个
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.whileTRUE
leaesi,[ebx+offset _WinName]
_invoke [ebx+_KnlFindWindow],NULL,esi
.if ! eax
lea esi,[ebx+ offset _FileName]
_invoke [ebx+_KnlWinExec],esi,SW_SHOWNORMAL
.endif
_invoke[ebx+_KnlSleep],110h
.endw
ret
_WinMainendp
REMOTE_CODE_ENDequ this byte
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;REMOTE_CODE_LENGTH 获取整个插入代码的长度,在nodead.asm中,VirtualAllocEx 中制定远程线程
;长度可以使用此参数
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
REMOTE_CODE_LENGTHequ offset REMOTE_CODE_END - offset REMOTE_CODE_START