一、前言
由于项目需要,最近研究了一下跨进程通讯改写第三方程序中的方法(运行中),把自己程序中的目标方法直接覆盖第三方程序中的方法函数;一直没有头绪,通过搜索引擎找了一大堆解决方案,资料甚是稀少,最后功夫不负有心人,经过两天的研究,终于在github 上找到两个开源的代码,通过两个开源代码结合起来即可实现我的需求。下面进一步来分析实践原理,后面会把源代码地址贴上来;
通过该文章分享,你会知道怎样通过注入一个dll模块改写第三方运行的程序中的某个方法,在里面实现自己的业务,这个场景在做外挂程序中特别实用!!!
二、场景
假如有一个第三方应用程序,这时候需要对第三方应用程序进行方法拦截,比如第三方应用程序中的某个操作需要用我们的业务覆盖掉他们的业务,那这种情况下我们有什么好的方案解决呢?我们不可能修改第三方程序的代码,那有什么方案可以解决呢?其实我们还是有办法进行”修改“第三方程序的代码的,怎么”修改“呢,请看下面实践原理,下面带你走入不一样的代码世界!!!!
三、实践
原理简化图:
这里实践我就直接写两个客户端程序来进行代码上的演示
3.1. 实现原理
-
Hook 目标方法:
需要改写拦截第三方程序的指定的方法,那就得需要Hook 该方法,经过查找资料在github上找到开源代码DotNetDetour
,但是开源作者是从.net framework 4.5开始支持,不支持.net framework 4.0, 我的需求需要运行在老爷机xp 上,故必须要支持4.0 的框架,所有我fork了一份把源代码做了修改支持到了.net framework 4.0 框架,fork 源代码地址:https://github.com/a312586670/DotNetDetour -
Inject 注入dll到目标进程
写好针对目标进程的方法Hooke dll 模块后需要考虑把该dll模块注入到第三方程序进程中,这样才可以实现完全的hook成功,改写目标进程的方法,我这里使用fastWin32 开源代码,代码地址如下:https://github.com/a312586670/FastWin32
3.2 创建第三方程序Demo
这里为了演示,我自己创建了一个目标客户端程序,主要有如下核心代码方法:
public class ProcessService
{
public string GetProcessInfo()
{
return "这是TargetClient 客户端(第三方程序)";
}
public ProcessResponse GetProcessInfo(ProcessRequest request)
{
return new ProcessResponse()
{
Name = "这是TargetClient 客户端(第三方程序)",
Version = request.Version
};
}
}
UI界面交互代码如下:
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnInfo_Click(object sender, RoutedEventArgs e)
{
var service = new ProcessService();
this.txtInfo.Text = service.GetProcessInfo();
}
private void btnComplateInfo_Click(object sender, RoutedEventArgs e)
{
var service = new ProcessService();
var response = service.GetProcessInfo(new ProcessRequest() { Version = "v-Demo 1.0 版本" });
this.txtInfo.Text = response.Name + response.Version;
}
}
上面代码中有两个按钮事件,分别调用了ProcessService 的两个方法,我们先来运行目标客户端Demo程序,分别点击两个按钮运行结果如下:
3.3 创建核心Hook类库
好了,上面我们的目标第三方Demo程序已经写好了,接下来我们需要写一个核心的Jlion.Process.HookCore
类库 改写目标的ProcessService
的两个方法。
我这里建了一个Jlion.Process.HookCore
类库,通过nuget包引用我fork 后的DotNetDetour
类库,如下图:
应用成功后我们建立核心的hook 方法,代码如下:
public class ProcessHookService : IMethodHook
{
[HookMethod("Jlion.Process.Target.Client.ProcessService", null, null)]
public string GetProcessInfo()
{
TextHelper.LogInfo($"这是Jlion.Process.HookCore.HookService dll. 改写TargetClient 客户端 的GetProcessInfo 方法后得到的结果");
return "这是Jlion.Process.HookCore.HookService dll. 改写TargetClient 客户端 的GetProcessInfo 方法后得到的结果";
}
[OriginalMethod]
public string GetProcessInfo_Original()
{
return null;
}
[HookMethod("Jlion.Process.Target.Client.ProcessService", null, null)]
public object GetProcessInfo([RememberType("Jlion.Process.Target.Client.Model.ProcessRequest", false)] object request)
{
var json = JsonConvert.SerializeObject(request);
TextHelper.LogInfo($"json:{json}");
var name = "这是Jlion.Process.HookCore.HookService dll. 改写TargetClient 客户端的GetProcessInfo(obj)后得到的结果";
return new ProcessResponse()
{
Name = name,
Version = "改写的dll 版本"
};
}
[OriginalMethod]
public object GetProcessInfo_Original([RememberType("Jlion.Process.Target.Client.Model.ProcessRequest", false)] object request)
{
return null;
}
}
我这里就不详细的写DotNetDetour
的使用,需要知道它的使用可以访问 https://github.com/a312586670/DotNetDetour 查看具体的文档
核心的Jlion.Process.HookCore
hook 类库 也已经创建完了,接下来还需要创建一个初始化Hook
的服务类(特别重要),并且还必须是静态
方法,代码如下:
public class HookService
{
/// <summary>
/// Hook 初始化
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
public static int Start(string msg)
{
try
{
TextHelper.LogInfo("开始"+msg);
MethodHook.Install();
}
catch
{
return -1;
}
return 1;
}
}
到这一步基本上Jlion.Process.HookCore
Hook 核心的类库已经创建完了
3.4 模块注入客户端程序
创建客户端后需要引用FastWin32
类库,如下图:
客户端注入Dll核心代码如下:
public class InjectService
{
//注入的核心dll 路径
public static string path = AppDomain.CurrentDomain.BaseDirectory+ "Jlion.Process.HookCore.dll";
/// <summary>
/// 进程id
/// </summary>
public static uint pid = 0;
/// <summary>
/// 启动
/// </summary>
public static void Start()
{
Inject();
}
#region 私有方法
private static void Inject()
{
try
{
Injector.InjectManaged(pid, path, "Jlion.Process.HookCore.HookService", "Start", "ss", out int returnValue);
}
catch (Exception ex)
{
}
}
#endregion
}
代码中核心的代码是Injector.InjectManaged()
,该方法有如下两个重构方法:
参数说明:
- processId:目标进程的进程id ->pid
- assemblyPath:核心Hook 注入的dll 绝对路径
- typeName:Hook 初始化方法的命名空间,一般注入一个模块dll后需要执行的入口初始化方法,这里是Hook 核心dll 中的HookService.Start 方法的命名空间(Jlion.Process.HookCore.HookService)
- methodName : 注入后执行的方法名称
- argument : 方法所需要的参数
- returnValue:返回注入后运行的方法返回值
客户端UI 核心代码如下:
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnInject_Click(object sender, RoutedEventArgs e)
{
InjectService.pid = Convert.ToUInt32(txbPid.Text.Trim());
InjectService.Start();
}
}
这里核心的注入Client Demo 也写完了,我们把注入的客户端也运行起来,输入目标的进程pid(也可以程序中查找目标进程Id),运行后再来执行上面创建的第三方程序的两个按钮,结果如下:
通过编写客户端程序点击注入dll后,再点击第三方程序的两个按钮事件,结果如下:
可以看到点击后,运行的结果已经被动态注入的Jlion.Process.HookCore.dll
改写了,不过上面的代码也可以改写后同时还运行原有目标的方法就是通过调用'_Original'后缀结尾的方法,方法体返回null即可。
四、总结
通过DotNetDetour
框架可以编写对目标进程的方法进行Hook 重写,使用新的方法覆盖第三方进程的方法,也可以继续执行第三方的方法。
通过FastWin32
调用Win32 API 把开发的dll模块注入到第三方进程中,同时注入后执行初始化方法,可以进行原有的Hook方法进行覆盖。
到这里是不是感觉很神奇,它可以在以下场景中使用:
- 想必大家想到的就是外挂程序,通过改写目标程序的方法进行外挂处理,写上自己的覆盖业务
- 灰产地带比较实用
- 破解第三方收费软件等等用途
感兴趣的朋友可以下载Demo 源代码玩一玩:
github 源代码地址:https://github.com/a312586670/processClientDemo