您的位置:首页 > 其它

远线程技术之一--DLL注入

2012-08-03 16:38 323 查看
WinNT/Win2000/WinXP中的远线程技术之一--DLL注入

什么是远线程?我们知道用CreateThread可以在当前进程里建立一个线程,远线程与此类似,只不过是在其他进程中建立一个线程,用API函数CreateRemoteThread。这个远线程建立后就与建立它的进程无关了,而是进入了另外一个进程。举例说,进程A可以在进程B中建立一个远线程,这个远线程就是进程B中的线程了,而此时如果进程A结束了,也不会影响到那个远线程的运行,除非进程B也结束了,那个远线程才会结束。怎么样,是不是很神奇啊?现在我们来看看怎么建立远线程。

最简单的远线程技术是DLL注入。好,先从这个讲起。所谓DLL注入就是将一个DLL放进某个进程的地址空间里,让它成为那个进程的一部分。要实现DLL注入,首先需要打开目标进程。

hRemoteProcess = OpenProcess( PROCESS_CREATE_THREAD | //允许远程创建线程

    PROCESS_VM_OPERATION | //允许远程VM操作

    PROCESS_VM_WRITE, //允许远程VM写

    FALSE, dwRemoteProcessId )

由于我们后面需要写入远程进程的内存地址空间并建立远程线程,所以需要申请足够的权限(PROCESS_CREATE_THREAD、VM_OPERATION、VM_WRITE)。

如果进程打不开,以后的操作就别想了。进程打开后,就可以建立远线程了,不过别急,先想想这个远线程的线程函数是什么?我们的目的是注入一个DLL。而且我们知道用LoadLibrary可以加载一个DLL到本进程的地址空间。于是,自然会想到如果可以在目标进程中调用LoadLibrary,不就可以把DLL加载到目标进程的地址空间了吗?对!就是这样。远线程就在这儿用了一次,建立的远线程的线程函数就是LoadLibrary,而参数就是要注入的DLL的文件名。(这里需要自己想一想,注意到了吗,线程函数ThreadProc和LoadLibrary函数非常相似,返回值,参数个数都一样)
还有一个问题,LoadLibrary这个函数的地址在哪儿?也许你会说,这个简单,GetProcAddress就可以得出。于是代码就出来了。

char *pszLibFileRemote= "my.dll ";

PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle( "Kernel32 "), "LoadLibraryA ");

CreateRemoteThread( hRemoteProcess, NULL, 0, pfnStartAddr, pszLibFileRemote, 0, NULL);

但是不对!不要忘了,这是远线程,不是在你的进程里,而pszLibFileRemote指向的是你的进程里的数据,到了目标进程,这个指针都不知道指向哪儿去了,同样pfnStartAddr这个地址上的代码到了目标进程里也不知道是什么了,不知道是不是你想要的LoadLibraryA了。但是,问题总是可以解决的,Windows有些很强大的API函数,他们可以在目标进程里分配内存,可以将你的进程中的数据拷贝到目标进程中。因此pszLibFileRemote的问题可以解决了。

char *pszLibFileName= "my.dll ";//注意,这个一定要是全路径文件名,除非它在系统目录里;原因大家自己想想。

//计算DLL路径名需要的内存空间

int cb = (1 + lstrlenA(pszLibFileName)) * sizeof(char);

//使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名缓冲区

pszLibFileRemote = (char *) VirtualAllocEx( hRemoteProcess, NULL, cb, MEM_COMMIT, PAGE_READWRITE);

//使用WriteProcessMemory函数将DLL的路径名复制到远程进程的内存空间

iReturnCode = WriteProcessMemory(hRemoteProcess, pszLibFileRemote, (PVOID) pszLibFileName, cb, NULL);

OK,现在目标进程也认识pszLibFileRemote了,但是pfnStartAddr好像不好办,我怎么可能知道LoadLibraryA在目标进程中的地址呢?其实Windows为我们解决了这个问题,LoadLibraryA这个函数是在Kernel32.dll这个核心DLL里的,而这个DLL很特殊,不管对于哪个进程,Windows总是把它加载到相同的地址上去。因此你的进程中LoadLibraryA的地址和目标进程中LoadLibraryA的地址是相同的(其实,这个DLL里的所有函数都是如此)。至此,DLL注入结束了。

参考文献:《Windows核心编程技术》

WinNT/Win2000/WinXP中的远线程技术之二--甩掉那个DLL

在《远线程技术之一》里我们已经能够将一个DLL注入到其他进程里了,但用某些工具(例如本站的DllShow)可以看到被注入的DLL的详细情况,比如DLL所在目录等。本文将讲述另一种建立远线程的方法,直接在其他进程里注入代码,而不需要什么DLL。

回忆一下DLL注入的方法,需要向目标进程写入一些数据(如DLL的全路径文件名),既然能写入数据,那么就可以写入执行代码。这样就可以不借助于DLL在其他进程里建立一个远线程了。

基本思路就是这样,写一个线程函数,然后把整个线程函数写入目标进程,然后启动它。当然,怎么确定线程函数在内存中的大小是个难题,我的方法就是用函数指针的差值再加上1024字节,这个方法肯定不好,但我也没想到什么好办法,谁有好办法可以给我留言。

这是我写的例子,远线程启动后每一秒响铃一次(BEEP)

参考文献:本站的《远线程技术之一》,农民网站: http://nongmin-cn.8u8.com/index.htm
WinNT/Win2000/WinXP中的远线程技术之三--DLL回来了

在《远线程技术之二》里,我们已经可以不借助DLL来直接向其他进程注入代码了,但是这个方法实在是太麻烦了,因为几乎所有的函数都要动态调用,写代码很费时间。因此想到了先将DLL注入到目标进程并得到一个启动函数的地址(启动函数就是你自己写在DLL中的一个函数,调用它可以完成你需要的功能),然后将DLL所占的内存做一个拷贝,卸载DLL,再把拷贝还原到原来DLL所占的那一块内存,这样就好像DLL仍然存在一样,然后就可以通过刚才的启动函数的地址启动你的函数了。这种方法的好处是DLL中的函数调用都是操作系统管理,不需要自己动态调用,编写容易。

这种方法需要得到的信息主要有三个,1)DLL的起始地址,这其实就是LoadLibrary的返回值,2)启动函数的地址,这可以通过GetProcAddress得到,3)DLL所占内存大小,我调用了一个API函数GetModuleInformation,根据MSDN的说明好像可以,这个函数只有WinNT4以上版本才能使用,不过反正CreateRemoteThread也只能用于WinNT,所以就无所谓了。而我参考的农民的代码是读取文件得到这个值的。

其他就没有什么特别难的地方了,如果能构看懂《远线程技术之二》的代码,那么这个也不会有什么问题了。

我写了个例子,可以把你自己写的DLL注入到某个进程,并执行你在DLL中输出的函数Start(),以下是DLL例子的代码。

//---------------------------------------------------------------------------

//这是一个时间服务器的例子

#include <windows.h>

#include <winsock.h>

#include <stdio.h>

char *weekday[7]={ "Sun ", "Mon ", "Tue ", "Wed ", "Thu ", "Fri ", "Sat "};

char *month[12]={ "Jan ", "Feb ", "Mar ", "Apr ", "May ", "Jun ", "Jul ", "Aug ", "Sep ", "Oct ", "Nov ", "Dec "};

DWORD WINAPI MyThreadProc(LPVOID lpParameter)

{

int sock;

struct sockaddr_in server;

int msgsock;

char systime[1024];

int rval;

WSADATA WsaData;

SYSTEMTIME t;

if(WSAStartup(MAKEWORD(2,0),&WsaData)==-1)

return 1;

sock=socket(AF_INET,SOCK_STREAM,0);

if(sock <0)

return 2;

server.sin_family =AF_INET;

server.sin_port =htons(13);

server.sin_addr.s_addr =INADDR_ANY;

if(bind(sock,(const struct sockaddr *)&server,sizeof(server)) <0)

return 3;

listen(sock,SOMAXCONN);

while(1)

{

msgsock=accept(sock,NULL,NULL);

GetSystemTime(&t);

sprintf(systime,

"%s %s %02d %02d:%02d:%02d %04d\n ",

weekday[t.wDayOfWeek],

month[t.wMonth-1],

t.wDay ,

t.wHour ,

t.wMinute ,

t.wSecond ,

t.wYear );

send(msgsock,systime,lstrlen(systime),MSG_OOB);

closesocket(msgsock);

}

return 0;

}

extern "C " __declspec(dllexport) __stdcall void Start()//这个是需要导出的启动函数

{

DWORD TID;

CreateThread(NULL,0,MyThreadProc,NULL,0,&TID);

}

int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)

{

return 1;

}

//---------------------------------------------------------------------------

参考文献:本站的《远线程技术之二》,农民网站: http://nongmin-cn.8u8.com/index.htm
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: