在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下的没有什么区别。。。。