将dll文件注入到其他进程中的一种新方法

摘要:
理论上我们没有精确的办法来确定我们的HookDLL是否已经注入到我们想要的进程中。另外一种最常见的方法是使用函数CreateRemoteThread,在其他进程中开启一个线程来装载DLL。本文将讨论一种将动态连接库注入到其他进程中的一种新方法。在这里我们将向读者演示我们是如何将DLL注入到Explorer.exe进程中!结果是Explorer.exe丝毫没有感觉到自己被中断过。源代码的功能是将我们的DLL注入到Explorer.exe中,在InjectDll.dll中我们创建了一个新的线程,然后在屏幕的左上角显示当前的时间。

http://www.45it.com/windowszh/201212/33946.htm

http://www.hx95.cn/Article/OS/201212/65095.html

我们知道将动态连接库注入到其他进程中有很多种方法。最常见的方法是使用钩子函数(Hook),但是这种方法主要有两个缺点:第一如果某个进程没有加载User32.dll,那么Hook DLL将永远也不会被加载。第二Hook DLL加载的时机问题,只有在进程发出User32调用的时候, Hook DLL才有可能被加载。也就是说假设进程正在进行复杂的数值计算而没有时间进行消息调用的时候,Hook DLL是不会被加载。理论上我们没有精确的办法来确定我们的Hook DLL是否已经注入到我们想要的进程中。另外一种最常见的方法是使用函数CreateRemoteThread,在其他进程中开启一个线程来装载DLL。应该说这是一种比较完美的解决放案,这种方法避免了上述使用钩子函数的所有缺点,但是遗憾的是这个函数只能使用在WinNT/2000下。

本文将讨论一种将动态连接库注入到其他进程中的一种新方法。它的思路与使用函数CreateRemoteThread的方法相类似,只不过可以使用在Win9x,Win2k,WinXP等操作系统下。在这里我们将向读者演示我们是如何将DLL(InjectDll.dll)注入到Explorer.exe进程中!

程序的思路如下

1:得到Explorer.exe进程中任意一个线程的ID.

2:根据这个线程的ID,得到这个线程的句柄Handle

3:挂起这个线程,并保存线程当前的“上下文”

4:改变这个线程的EIP指针,使它指向我们装载DLL的函数(InjectCodeFun),然后恢复这个线程。

5:我们的装载DLL的函数运行完成后,再次挂起这个线程,使用我们以前保存的“上下文”,恢复这个线程到它被改变前的状态,并继续运行。

经过上述几个步骤,Explorer.exe进程中就会替我们装载我们的DLL(InjectDll.dll)了,有趣的是Explorer.exe对此丝毫没有察觉任何异常!

下面我们将详细解释一下如何编程实现上述过程。

步骤1的实现是很容易的,我们只需要调用ToolHelp的函数就可以得到我们所要得,这里我们就不详细说明了,请参考源代码中GetProcessID, GetThreadID 两个函数。

步骤2就比较麻烦了,在Win9x中没有提供一个函数可以由Thread ID得到Thread Handle(幸运的是Win2K提供这种功能)。好在我们在国外一些BBS上可以找到这个函数,它使用了一些未公开的结构,本文的目的不是讨论这个问题,读者如果有兴趣的话,可以参考我们的源代码OpenThread2函数。这个函数的作用就是传入一个Thread ID参数返回相应的 Thread Handle。

步骤3 的实现也是很容易和规范的,我们可以用SuspendThread,GetThreadContext等SDK函数轻松完成。

步骤4 这个步骤是最重要的步骤了。为了说明方便,我们将引用我们源代码中的语句,请读者参考源代码中InjectCodeIntoThread 函数。

首先改变线程的EIP指针,我们可以用下列代码完成

ThreadContext.Eip = (DWORD)m_lpCodeBase;

SetThreadContext(m_hInjectThread,&ThreadContext);

变量m_lpCodeBase指向我们的装载DLL的函数(InjectCodeFun)的首地址。

这里最关键的部分是我们如何产生我们的装载DLL的函数(InjectCodeFun)。注意我们不能简单地在我们的程序里写一个函数,然后将它的首地址赋值给EIP。这是因为装载DLL的函数是要运行在Explorer.exe地址空间中的,如果我们使用自己地址空间中的函数的话,那么必然会导致系统崩溃。解决的办法是将我们写的装载DLL函数(InjectCodeFun)放在所有程序共享的地址空间中去,在Win9x中0x80000000 ~ 0xFFFFFFFF这段地址就是我们想要的共享地址空间,那么如何将我们写的装载DLL函数放在这段地址空间呢 ?方法有很多,我们使用一种规范的方法“内存映像文件”来解决这个问题。我们通过函数CreateFileMapping来分配一段共享地址空间,然后将我们写的装载DLL函数拷贝到这段地址空间中去。具体代码请参源代码中InitInject函数。

在我们写的装载DLL函数(InjectCodeFun)中还有两个问题我们需要解释一下,第一 在这个函数中我们不能使用任何我们自己程序中定义的变量,道理跟上面讲的一样,因为地址空间不同。还有我们不能直接调用函数,例如在InjectCodeFun中直接使用LoadLibray。这是因为如果直接使用LoadLibray那么就需要经过程序的Import表,跳转一下才能到达真正的Windows的LoadLibray函数。但是不同的进程有不同的Import,所以我们不能直接调用函数。我们可以使用一种叫做“动态构造函数”的技术来创建我们的函数。首先用GetProcAddress得到函数LoadLibray的直接地址,然后在调用LoadLibray的地方,使用一个特殊的数字来代替它如 0x11111111,最后在将我们的函数拷贝到共享地址空间之后,搜索共享内存找到这个特殊数字,用我们先前得到的正确地址替换它既可。第二个有趣的现象是我们所写的装载DLL函数(InjectCodeFun)是不应该返回的。这是因为这个函数是在Explorer的线程中运行的,我们不知道堆栈的正确内容,不知道ESP所指向的地址是什么,如果函数返回的话,我们将失去对程序的控制。我们的办法是,当调用完LoadLibray之后,向我们的主程序发送一个自定义消息,通告我们的程序已经完成装载任务,然后让线程进入死循环状态。

步骤5当我们的程序接受到了自定义消息后,就会再次挂起这个线程,把我们以前保存的线程的“上下文”用函数SetThreadContext恢复,然后恢复运行这个线程。结果是Explorer.exe丝毫没有感觉到自己被中断过。

以上就是我们所介绍的方法,读者可以参考我们的源代码来具体了解上述方法。源代码的功能是将我们的DLL(InjectDll.dll)注入到Explorer.exe 中,在InjectDll.dll中我们创建了一个新的线程,然后在屏幕的左上角显示当前的时间。源代码分为Win9x版本和Win2k版本,这两个版本的主要差别是分配共享内存的方法不同而已。源代码已经在PWn98,PwinMe,Win2k,WinXP等操作系统下,使用VC6编译通过

读者可以在下列网址:http://webaide.myetang.com/或http://netaide.top263.net/的“下载”页面中找到源代码。
作者:RobinHao(webaide2k@sina.com)
转载请征得作者同意.

免责声明:文章转载自《将dll文件注入到其他进程中的一种新方法》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Oracle10g.CentOS6安装_遇到的问题(01)sql server 的临时表和表变量下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

java--jvm启动的参数

java启动参数共分为三类其一是标准参数(-),所有的JVM实现都必须实现这些参数的功能,而且向后兼容;其二是非标准参数(-X),默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足,且不保证向后兼容;其三是非Stable参数(-XX),此类参数各个jvm实现会有所不同,将来可能会随时取消,需要慎重使用; 标准参数中比较有用的: verbos...

如何使用JDK自带工具JConsole

前言 jconsole.exe为jdk自带的监控工具,操作简便,比较容易上手。jconsole可以实时的监控Java程序在运行过程中的内存,cpu,线程的使用情况,并可以对加载的相关类进行分析。下面开始一起看看如何使用jconsole吧! 打开jconsole步骤: 1、首先进入到jdk的安装目录bin目录下;双击“jconsole.exe”打开jcons...

生产者和消费者模型

生产者和消费者模型 线程通信:不同的线程执行不同的任务,如果这些任务有某种关系,各个线程必须要能够通信,从而完成工作。线程通信中的经典问题:生产者和消费者问题 模型: 这个模型也体现了面向对象的设计理念:低耦合 也就是为什么生产者生产的东西为什么不直接给消费者,还有经过一个缓冲区(共享资源区) 这就相当于去包子店吃包子,你要5个包子,老板把5个人包子放在...

操作系统——进程,线程,锁

基本概念 状态、地址空间 三种基本状态 —— 就绪、运行、阻塞 进程控制块PCB(Process Control Block) 进程描述信息(如PID); 进程控制&管理信息(状态、优先级等); 源分配清单(地址空间状况、fd等); 处理其相关信息(各寄存器的值等) 进程存在的标识,在Linux系统中是task_struct,task_s...

C语言多线程编程 死锁解析

1.假设有两个线程   A线程负责输出奇数。B线程负责输出偶数。 2.当A线程进入锁定状态是,主线程突然异常将A线程停止,这时将导致B线程也无法继续执行,处于死锁状态。如下代码: #include <stdio.h> #include <stdlib.h> #include <pthread.h> pthread_mu...

Windows中的线程命名杂谈

Windows允许您为进程中的线程指定名称,然后调试器可以显示这些名称。这是一个很好的解决方案,但这是一个很好的解决方案。Windows 10 Creators更新(SetThreadDescription)中添加了一个新的线程命名API。Chrome现在使用SetThreadDescription来命名它的线程(当这个函数可用时)。chromiumrep...