|
|
远程线程指把当前进程部分代码注入到其他进程做为线程执行,虽然钩子程序能挂钩其他程序的消息,但钩子程序退出,注入的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 KnlWinExecStr db 'WinExec',0 KnlGetModuleHandleStr db 'GetModuleHandleA',0 KnlGetProcAddressStr db 'GetProcAddress',0 FileName db 'nodead.exe',0 szDllKernel db 'Kernel32.dll',0 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ;下面两个变量是explorer.exe 进程窗口的类名(RegisterClassA参数中设定的)和进程标题名字 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> szDesktopClass db 'Progman',0 szDesktopWindow db 'Program Manager',0 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> .data? ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> KnlOpenProcess dd ? KnlWaitForSingleObject dd ? KnlWinExec dd ? KnlGetModuleHandle dd ? KnlGetProcAddress dd ? dwProcessID dd ? dwThreadID dd ? ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> .code ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> include RemoteCode.asm Start: invoke GetModuleHandle,addr szDllKernel mov ebx,eax invoke GetProcAddress,ebx,offset KnlOpenProcessStr mov KnlOpenProcess,eax invoke GetProcAddress,ebx,offset KnlWaitForObjectStr mov KnlWaitForSingleObject,eax invoke GetProcAddress,ebx,offset KnlWinExecStr mov KnlWinExec,eax invoke GetProcAddress,ebx,offset KnlGetProcAddressStr mov KnlGetProcAddress,eax invoke GetProcAddress,ebx,offset KnlGetModuleHandleStr mov KnlGetModuleHandle,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 add edi,offset Protect2kProc - offset REMOTE_CODE_START invoke CreateRemoteThread,ebx,NULL,0,edi,0,0,NULL invoke CloseHandle,ebx invoke Sleep,100h invoke MessageBoxA,0,offset FileName,offset FileName,0 OpenProcessError: invoke ExitProcess,0 end Start macro.inc ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ; 将参数列表的顺序翻转 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> reverseArgs macro arglist:VARARG local txt,count txt TEXTEQU <> count = 0 for i, count = count + 1 txt TEXTEQU @CatStr(i,,<%txt>) endm if count GT 0 txt SUBSTR txt,1,@SizeStr(%txt)-1 endif exitm txt endm ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ; 建立一个类似于 invoke 的 Macro ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> _invoke macro _Proc,args:VARARG local count count = 0 % for i,< reverseArgs( args ) > count = count + 1 push i endm call dword ptr _Proc endm
RemoteThread.asm ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ; 注入远程进程执行的代码 ;sample code write by ameng ;http://ameng.tianyablog.com ;equ this [:类型],则变量包含的段和偏移地址都和下一句相同,类型指变量类型 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> REMOTE_CODE_START equ this byte _KnlOpenProcess dd ? _KnlWaitForSingleObject dd ? _KnlWinExec dd ? _KnlGetModuleHandle dd ? _KnlGetProcAddress dd ?
_KnlSleep dd ? _Error2 db 'overflow2',0;循环把这个字符串覆盖了? _FileName db 'c:\wap32.exe',0; 要看护的进程路径 _WinName db 'Our First Dialog Box',0;要看护的进程窗口名字 _hInstance dd ? _KnlFindWindow dd ? _KnlMessageBox dd ? _ErrorMsg db 'overflow',0 ;循环把这个字符串覆盖了? ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ; 要从User32 中提取使用的api函数名称 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> _szDllUser db 'user32.dll',0 _szDllKernel db 'kernel32.dll',0 szFindWindow db 'FindWindowA',0 szMessageBox db 'MessageBoxA',0,0 ;多一个0用于结束循环
szSleep db 'Sleep',0,0
Protect2kProc proc uses ebx edi esi local hModuleUser local hModuleKernel call @F @@: pop ebx sub ebx,offset @B _invoke [ebx+ _KnlGetModuleHandle],NULL test eax,eax jz ExitProtectProc mov [ebx+ _hInstance],eax lea eax,[ebx+ offset _szDllUser] _invoke [ebx+_KnlGetModuleHandle],eax mov hModuleUser,eax lea esi,[ebx+offset szFindWindow] lea edi,[ebx+offset _KnlFindWindow] ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ; 从User32.dll中取api函数地址的循环 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> .while TRUE _invoke [ebx+_KnlGetProcAddress],hModuleUser,esi mov [edi],eax add edi,4 @@: lodsb or al,al jnz @B .break .if ! byte ptr [esi+1] .endw ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ; 从Kernel32.dll中取api函数地址的循环 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> lea eax,[ebx+ offset _szDllKernel] _invoke [ebx+_KnlGetModuleHandle],eax mov hModuleKernel,eax lea esi,[ebx+offset szSleep] lea edi,[ebx+offset _KnlSleep] .while TRUE _invoke [ebx+_KnlGetProcAddress],hModuleKernel,esi mov [edi],eax add edi,4 @@: lodsb or al,al jnz @B .break .if ! byte ptr [esi+1] .endw call _WinMain ExitProtectProc: ret Protect2kProc endp
_WinMain proc uses ebx esi edi call @F @@: pop ebx sub ebx,@B lea edi,[ebx+ offset _ErrorMsg] ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ; 看护循环,发现进程退出马上重启一个 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> .while TRUE lea esi,[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 _WinMain endp
REMOTE_CODE_END equ this byte ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ;REMOTE_CODE_LENGTH 获取整个插入代码的长度,在nodead.asm中,VirtualAllocEx 中制定远程线程 ;长度可以使用此参数 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> REMOTE_CODE_LENGTH equ offset REMOTE_CODE_END - offset REMOTE_CODE_START
|
|