远程线程技术

来源:互联网 发布:29节的纳尔逊 知乎 编辑:程序博客网 时间:2024/06/06 04:22

在Windows的平台上,DLL是系统的基础,那么我们首先就利用DLL来完成我们的远程
线程。

在DLL中,有一个入口点函数:DllMain,其原形如下:
BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved)
{
        switch (ul_reason_for_call)
        {
                case DLL_PROCESS_ATTACH:
                        break;
                case DLL_PROCESS_DETACH:
                        break;
                case DLL_THREAD_ATTACH:
                        break;
                case DLL_THREAD_DETACH:
                        break;
        };
        
    return TRUE;
}

下面我们来说说它的参数ul_reason_for_call,这个参数就是告诉dll,系统为什么调用这个函数:
DLL_PROCESS_ATTACH: 当系统第一次装载DLL到进程空间时,就发出这个通知;如果以后在该进程中
                    继续调用LoadLibrary或LoadLibraryEx函数,系统将不再发出这个通知,只是
                    给DLL的引用计数加1
DLL_PROCESS_DETACH: 当DLL从进程空间中卸载时,系统将调用DllMain函数,传递DLL_PROCESS_DETACH
                    给ul_reason_for_call

其它的两个消息和系统调用该函数的详细资料,可参考《windows核心编程》第20章:DLL的高级操作技术
这个函数的返回值表示DLL是否初始化成功(如果ul_reason_for_call不是DLL_PROCESS_ATTACH,系统将
忽略这个返回值)。

现在就可以使用DLL_PROCESS_ATTACH这个消息来完成我们想要做的事。
我们如果能让目标进程调用我们自己的的DLL,那么,我们就可以在远程进程中做一些其它的事了。。
:P

OK,让我们继续:
在Win2k中,我们可以使用CreateRemothThread实现成我们的想法。
CreateRemothThread的说明:
原型:HANDLE WINAPI CreateRemoteThread(HANDLE hProcess,
                                       LPSECURITY_ATTRIBUTES lpThreadAttributes,
                                       DWORD dwStackSize,
                                       LPTHREAD_START_ROUTINE lpStartAddress,
                                       LPVOID lpParameter,
                                       DWORD dwCreationFlags,
                                       LPDWORD lpThreadId);
执行成功,返回目标进程中的线程handle。
hProcess 目标进程的Process handle
lpThreadAttributes 远程线程的安全属性。如果为NULL,则继承父进程的安全属性
dwStackSize 远程线程的堆栈大小。如果为0则为1M(系统默认为1M)
lpStartAddress 远程线程代码的起始地址。
lpParameter 远程线程的参数
dwCreationFlags 远程线程的的选项
lpThreadId 传出的参数,远程线程的Thread ID

好,有了这些东东,我们就可以在目标进程中执行LoadLibrary了,目标进程就可调用我们的DLL了。 :P
相关的资料我们在上面已经了解过了,下面我们来看看具体是怎么实现的;
1. 打开目标进程:
        //打开远程进程 
        hRemoteProcess = OpenProcess(PROCESS_CREATE_THREAD | //允许创建线程 
                                     PROCESS_VM_OPERATION | //允许VM操作 
                                     PROCESS_VM_WRITE,       //允许VM写 
                                     FALSE, dwRemoteProcessId );

2. 因为win2k的字符串使用unicode来表示的,所以呢?我们要把ascii表示的字符串转换成unicode的形式:
        //将DLL文件全路径的ANSI码转换成UNICODE码 
        iReturnCode = MultiByteToWideChar(CP_ACP,
                            MB_ERR_INVALID_CHARS,
                            lpDllFullPathName, 
                            strlen(lpDllFullPathName), 
                            pszUncodeDllName, MAX_PATH); 
                            
3. 因为是要在远程进程中执行的,那么所有的字符串、资源都要转换成目标进程的地址空间,怎么转换呢?
   没办法!!
   那怎么办呢?用WriteProcessMemory把这些东西写到目标进程中不就行了 :P

        //计算DLL路径名需要的内存空间 
        int cb = (1 + lstrlenW(pszUncodeDllName)) * sizeof(WCHAR);  //注意,这儿用的是是unicode码
        //在目标进程中分内存
        pszRemoteDLLPath = (PWSTR) VirtualAllocEx( hRemoteProcess, NULL, cb, 
                MEM_COMMIT, PAGE_READWRITE); 
        
        //将DLL的路径名复制到远程进程的内存空间 
        iReturnCode = WriteProcessMemory(hRemoteProcess, 
                pszRemoteDLLPath, (PVOID) pszUncodeDllName, cb, NULL); 
                
4. 得到LoadLibrary函数在目标进程中的地址
        PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE) 
                GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW"); 
                
5. 使用CreateRemoteThread函数创建远程线程
        //启动远程线程,通过远程线程调用用户的DLL文件
        //就相当于在目标进程中执行LoadLibrary("我们的DLL")
        //如果到这还不明白为什么调用LoadLibrary,大伙,板砖上呀!!! :)
        hRemoteThread = CreateRemoteThread( hRemoteProcess, NULL, 0, 
                pfnStartAddr, pszRemoteDLLPath, 0, NULL); 
                
6. OK,到现在为止,我们已经在目标进程中执行了我们的代码了,目标实现了,别急着走,还有些工作要做的:
        //等待远程线程退出
        WaitForSingleObject(hRemoteThread, INFINITE); 
        //清场处理 
        if (pszRemoteDLLPath != NULL) 
                VirtualFreeEx(hRemoteProcess, pszRemoteDLLPath, 0, MEM_RELEASE); 
        if (hRemoteThread != NULL) CloseHandle(hRemoteThread ); 
        if (hRemoteProcess!= NULL) CloseHandle(hRemoteProcess);         


最后,还有一些需要注意的地方:
你在目标进程中的程序不能出现运行时错误,出现了会怎么办呢?如果出现,windows就会告诉你
XX进程(这可是目标进程呀!)出现了非法操作......

如果你想使用这么方式来杀掉目标进程(嘿嘿,有些进程windows的任务管理器杀不掉,这种方式就可以
搞定!!),ok,你就可以go home了。不想出现这种情况了嘛,也go home,回家检查代码去。我也要
zzZZzz了。。


哎,别急,,98下怎么办?
98下嘛,没有CreateRemoteThread这上函数,不过呢。有一个未公布的的函数CreateKernelThread,这个
函数藏在KERNEL32.DLL里面,用GetProcAddress就可以找它出来了:
fCreateKernelThread = GetProcAddress(GetModuleHandle("KERNEL32.DLL"), "CreateKernelThread");
说明如下:
HANDLE CreateKernelThread(LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
这是调用的例子:
hThread=fCreateKernelThread(NULL, 0, (PTHREAD_START_ROUTINE) pRun, pRunParam, 0, &ThreadId);

其它的步骤嘛,和win2k下的没有什么区别。。。。