]进程注入是王道之为NhibernateProfiler增加“附加到进程”功能原理(源码)
[屌丝的逆袭系列]进程注入是王道之为NhibernateProfiler增加“附加到进程”功能-原理(源码)
上篇 我们一起看了附加到进程这个功能实现后的样子,这篇我们就来讲一下他的实现原理。如果你还没有看过上一篇里的功能介绍的话,建议 回去扫一眼 ,花不了二分钟的时间,要不然你继续往下看的话,会一头雾水的 。
从上篇的演示中,我们不难看出,要实现附加到进程的功能,至少需要解决两个问题。
一.如何把 HibernatingRhinos.Profiler.Appender.dll 送到目标进程,并在这个进程里调用 HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize() 让 NhibernatePorfiler 实现初始化
二. 如何给 NHProf 加一个热键,可以方便的呼出我们的附加到进程。
下面我们就来一个一个的解决这两个问题。
解决第一个问题用到的主要 技术就是进程注入, 要实现进程注入无非就是三种 方法 1. 利用全局钩子。 2 . 利用 CreateRemoteThread和LoadLibrary把你的 DLL 注入。 3. 用 CreateRemoteThread和WriteProcessMemory 直接往目标进程 写指令 ( 这个其实和第二种用的技术是一样的)。 在这里我们采用的是第一种方式 ,这是三种方式中最安全的方式,当然也是适应范围最小的,只能用于使用了 USER32 的进程中 ,也就是必须是有界面的程序。
进程注入的代码,我们就不细讲了,注释写的已经很详细了,你们可以自已看一看源码,我已经将他们封装在了 Injector 这个工程里,这是一个 VC CLR Library 的工程,如果你的 VS 没有装 C++ 语言的话,就直接引用 DLL 。编译后的 DLL ,我也拷贝了一份放在了附录根目录下的 Libs 文件夹里。名字就叫 Injector.dll 。在这里我们只简单的提一下他的用法。
Injector.dll 的使用很简单,不过在讲它如何用之前,我们还需要做一些假设。
首先我们假设,有一个名为 UI 的工程,他编译后生成的程序正是实现进程注入的主程序,在这个程序里,可以列出来系统里的所有进程,并且可以选则一个进程然后点击附加到进程按钮后就可以实现进程注入。
然后我们再假设,还有一个名为 ProcessViewer 的的工程,这是一个类库( Class Library )工程,我们正是要把这个工程编译后生成的 DLL ,注入到目标进程。
说到这里,如果已经打开了源码的朋友,可能已经看出什么了,是的,这些其实并不是什么假设,而是我们源代码里实际的项目 命名 。
选中的那个就是 UI 工程了,他上面的那个就是 ProcessViewer 工程。
从上图还可以看到,除了这两个工程外,还有好几个工程,这里我们来对他们作个简单的介绍。
DotNetHookLibrary : UI 调用了他里面的 AssembleViewer的IsDotNet方法来验证目标进程是否 .Net 程序 。
Injector : 这个就是上面介绍的实现注入的核心 DLL 了。其实就是先把这个工程生成的 DLL 注入到目标进程,再由这个 DLL 在目标进程里将我们要注入的 DLL 再加载进目标进程。
NHProfLancher : 这个是为了解决上面讲的第二个问题:加热键而写的一个 NHProf 的加载程序。这里就先不讲了,后面会提到。
Win32SDK : 封装 API 的一个类库工程,本来我的意思是 慢慢的把所有 API 都封装到这里面,那么以后只要引用这个工程就可以方便的调用 API 了,不过细想后发现其实意义不大,因为完全没有必要因为只使用了一个 API ,就引用这么一个庞大的类库,不过已经写了而且引用了,就放在这里了。
看完了上面的所有工程,细心的可能已经发现了,上节里我们使用的那个测试用的被注入的程序的工程这里并没有,是的,为了方便同时调用,我把那个工程放在了另外一个解决方案里,就在附录的根目录下的 测试 文件夹里,测试文件里有一个Test.sln 。打开它 。
这个就是测试用的被注入的目标程序了。
OKAY 描述到这里,基本上可以讲 Injector.dll 的使用方法了。
要利用 Injector.dll 实现进程的注入,首先你得在你的主程序里添加对他的引用 (Add Reference) 了 ,在我们这里,当然是在 UI 这个工程里添加对他的引用了。 能打开 VC CLR Library 类型工程的,直接引用工程,不行的, Browser 选中 Libs 下的 Injector.dll 。引用后,只要在需要的地方调用 App.Security.Injector.Launch就可以实现注入了。
我们先来看看 App.Security.Injector.Launch 的定义
// ----------------------------------------------------------------------------- // 方法描述 : 启动注入,此函数执行成功,注入便已成功 , 并会在目标进程执行 className.methodName // 参数 : // windowHandle 目标进程主窗体的句柄 // assembly 要注入到目标进程的Assembly // className 注入后,要执行的方法的类名 // methodName 注入后,要执行的方法名 // 返回值 : void // ----------------------------------------------------------------------------- void Injector::Launch(System::IntPtr windowHandle, System::Reflection::Assembly^ assembly, System::String^ className, System::String^ methodName)
再看看我们 UI 工程里的实际代码 (这段代码就在附加进程按钮的单击事件里 :) )
App.Security.Injector.Launch( targetProcess.MainWindowHandle, typeof (ProcessViewer.ProcessViewer).Assembly, typeof (ProcessViewer.ProcessViewer).FullName, " Entry " );
那么结合前面所讲的。这句执行完成后。ProcessViewer.ProcessViewer所在的 Assembly (就是我们的 ProcessViewer 工程编译后生成的 DLL 了) 就已经被注入到 主窗体句柄为 targetProcess.MainWindowHandle 的目标进程里了。并且会在目标进程里执行ProcessViewer.ProcessViewer类的 Entry 方法 。
当然要明白刚才讲的,还有二个疑问需要解决,第一个就是 targetProcess.MainWindowHandle 从那里来的。 这个其实不难理解,前面也讲了, UI 有列出所有进程的功能,而且还可以选择一个进程实现注入,那么这个 targetProcess 自然就是你选中的那个进程的对象了, targetProcess 的类型就是 Process 。第二个刚才你也没讲过引用了 ProcessViewer 那个工程 , 直接这样 typeof(ProcessViewer.ProcessViewer).Assembly 不会出错吗 ??? 这个吗,当然是要先引用一下 ProcessViewer 工程的,那么又有问题了, ProcessViewer 是要被注入目标进程执行的,那么有必要还要先要加载到我们这个进程里吗?答案是原则上是没有必要的,事实上我们只要知道了这个 Assembly 的路径就完全可以实现注入了,但在我们的工程里,把传给的 Lancher 的第二个参数,写成了 要传递一个 Assembly 对象,所以在我们的工程里是要引入一下的,这样写起来的代码,看起来 显得 更优雅一点。 :)
那么依次类推,要把HibernatingRhinos.Profiler.Appender.dll 注入到目标进程,并在目标进程里执行 HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize() 应该怎么做呢
App.Security.Injector.Launch( targetProcess.MainWindowHandle, typeof (HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler).Assembly, typeof (HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler).FullName, " Initialize " );
你的第一反应一定是这样写,原则上(汗 ~~~ ,又是原则上)这样写应该也是可以的,但实际上我们并没有这样做(事实上也不能这么,你试试就知道了),我们还是如前面描述的那些那样先把 ProcessViewer 送到了目标进程,然后在目标进程执行了 ProcessViewer 的 Entry 方法。然后才在 Entry 方法里加载了 HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler ,并利用反射原理执行了他的 Initialize 方法。我们来看看 ProcessViewer 的 Entry 代码。
public void Entry() { string path = this .OriginalPath + " \\HibernatingRhinos.Profiler.Appender.dll " ; string f = System.Environment.CurrentDirectory + " \\HibernatingRhinos.Profiler.Appender.dll " ; if (! File.Exists(f)) { File.Copy(path, f); } string className = " HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler " ; Assembly assembly = Assembly.LoadFile(f) ; Type type = assembly.GetType(className); MethodInfo mi = type.GetMethod( " Initialize " , BindingFlags.Public | BindingFlags.Static, null , Type.EmptyTypes, null ); mi.Invoke( null , null ); }
上面的 this.OriginalPath 是在 Injector.dll 里传过来,实际就是 ProcessViewer.dll 所在的目录。
在这段代码先将 this.OriginalPath 目录下的 HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.dll 先拷贝到目标进程所在的目录里(所以你必须在 UI 的工程里也引用一下 HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.dll ,这样才能保证 this.OriginalPath 的目录下有这个 DLL ),这是必须的,因为只有在目标进程里才有 Nhibernate 的相关 DLL ,而 HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler 的初始化是需要这些的,这也是我们不直接将 HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.dll 送到目标进程的原因之一 , 另外一个原因是你会发现如果有了 ProcessViewer 的这个 Entry 的话那么在将 HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.dll 加载到目标进程前后,可以更加方便的做一些其它处理,当然这里我们 没有做处理了,但是你要想的话,是很方便的,例如,注入后, 提示一下什么的 …… 当然这是闲话了,将 HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.dll 拷贝到目标进程的目录后,就是加载它,然后 就是 利用 反射,调用他的 Initialize 方法。 注意 ProcessViewer 的 Entry 是在目标进程执行的,所以这里的一切都是在目标进程执行 的
至此,我们的第一个问题就算解决了。 下面我们解决第二个问题。
其实刚才在描述我们源码的目录时,已经提到了,实现第二个问题的方法就是为 NHProf.exe 写一个 Lancher , 也就是我们的那个 NHProfLancher 工程了。
代码也不细讲了,看一看就明白了,不是很麻烦,在这里只要知道,在这个 L ancher里主要做了哪些工作就可以了。
这个 Lancher 的只要有两方面的工作 1 ,注册热键,并捕获热键来打开我们的附加到进程工具。 2. 在另外一个线程里,加载执行 NHProf.exe 并且一直等到 NHPorf.exe 退出为止, NHProf.exe 一旦退出,此线程立马执行 this.close 关闭本程序。然后在 Form_Closed 事件的处理函数里, 取消热键 。
附录(源码)
作者: Leo_wl
出处: http://www.cnblogs.com/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息查看更多关于]进程注入是王道之为NhibernateProfiler增加“附加到进程”功能原理(源码)的详细内容...