__in SIZE_T nSize,
__out SIZE_T *lpNumberOfBytesWritten
);
由于他们签名类似,此处放在一块介绍。
hProcess是用来标识远程进程的。
lpBaseAddress是在远程进程地址空间的地址,是VirtualAllocEx的返回值。
lpBuffer是在本进程的内存地址。此处也就是DLL路径名的地址。
nSize为要传输的字符串。
lpNumberOfByteRead和lpNumberOfByteWrite为实际传输的字节数。
注意:当调用WriteProcessMemory时有时会导致失败。此时可以尝试调用VirtualProtect来修改写入页面的属性,写入之后再改回来。
到此为止,看起来没啥东西了,但是还有一个比较隐晦的问题,如果不对PE文件格式和DLL加载的方式有所了解的话是很难发现的。
我们知道导入函数的真实地址是在DLL加载的时候获得的。加载程序从导入表取得每一个导入函数的函数名(字符串),然后在被加载到进程地址空间的DLL中查询之后,填到导入表的相应位置(IAT)的。也就是说在运行之前我们并不知道导入函数的地址(当然模块绑定过得除外)。那么程序代码中是如何表示对导入函数的调用呢?有没有想过这个问题呢。
你或许觉得应该是:CALL DWORD PTR[004020108] ( [ ]内仅表示导入函数地址,无实际意义)。
由于程序的代码在经过编译连接之后就已经确定,而导入表的地址如00402010是在程序运行的时候获得的。所以程序在调用导入函数的时候并不能这样实现。那到底是如何实现的呢?
[ ]内有一个确定的地址这是毋庸置疑的,但是他的值并不是导入函数的地址,而是一个子程序的地址。该子程序被称为转换函数(thunk)。这些转换函数用来跳转到导入函数。当程序调用导入函数时,先会调用转换函数,转换函数从导入表的IAT获得导入函数的真实地址时在调用相应地址。
所以对导入函数的调用形如如下的形式:www.2cto.com
CALL 00401164 ;转换函数的地址。
。。。。。。
:00401164
。。。。。
CALL DWORD PTR [00402010] ;调用导入函数。
分析到这儿,我们也可以明白为什么在声明一个导出函数的时候要加上_decllpec(dllimport)前缀。
原因是:编译器无法区分应用程序是对一般函数的调用还是对导入函数的调用。当我们在一个函数前加上此前缀就是告诉编译器此函数来自导入函数,编译器就会产生如上的指令。而不是CALL XXXXXXXX的形式。
所以在写一个输出函数的时候一定要在函数声明前加上修饰符:_decllpec(dllimport)。
言归正传.之所以说这么多,就是因为我们传给CreateRemoteThread的线程函数LoadLibrary*,会被解析成我们进程内的转换函数的地址。如果把这个转换函数的地址作为线程函数的起始地址很可能导致访问违规。解决方法是:强制代码略过转换函数而直接调用LoadLibrary*.
这可以通过GetProAddress来实现。
FARPROC WINAPI GetProcAddress(
__in HMODULE hModule,
__in LPCSTR lpProcName
);
hModule是模块句柄。标志某一模块。
lpProcName是该模块内某一函数的函数名。
它返回该函数在模块所属进程地址空间的地址。
如GetProcAddress(GetModuleHandle("Kernel.dll","LoadLibraryW"));
此语句取得LoadLibrary在Kernel.dll所在进程空间的真实地址。注意此时仅仅是取得在本进程Kernel.dll的地址和LoadLibraryW的地址。难道在远程进程内也是一样吗?
《windows核心编程》第五版589页第三段中说,”从作者的经验来看,Kernel.dll映射到每个进程的地址都是相同的。“基于此,我们可以认为,我们调用此语句是取得了Kernel.dll和LoadLibraryW在远程地址空间的地址。
到此为止,关于远程线程就介绍完毕。
参考自《windows核心编程》第五版 第二十二章 ,《加密与解密》第二版 段钢著,第十章
以上仅仅在参考各书籍的基础之上加以总结。如有错误,请不吝赐教。