theXappy/RemoteNET

GitHub: theXappy/RemoteNET

一个允许在不修改目标程序的前提下,以编程方式检查、创建并操作其他 .NET/C++ 进程中远程对象的进程交互库。

Stars: 46 | Forks: 3

![icon](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/145ed2908a013249.png) # RemoteNET [![NuGet](https://img.shields.io/nuget/v/RemoteNET)][nuget-link] 这个库允许你检查、创建并与其他 .NET/Visual C++ 进程中的远程对象进行交互。 目标应用程序不需要专门编译(或同意)来支持它。 ### 👉 立即尝试:下载 [RemoteNET Spy](https://github.com/theXappy/rnet-kit) 应用程序,查看此库的实际运行效果!👈 ## **支持的目标** ✅ .NET 5/6/7/8/9 ✅ .NET Core 3.0/3.1 ✅ .NET Framework 4.5/4.6/4.7/4.8(包含子版本) ✅ MSVC 编译的 C++(实验性) ## 在项目中包含此库 有两种方法可以获取该库: 1. 从 [NuGet][nuget-link] 获取 -或者- 2. 克隆此仓库,编译后在你的项目中引用 `RemoteNET.dll` 和 `ScubaDiver.API.dll`。 ## 编译 1. Clone 2. 初始化 git 模块(针对 `detours.net`) 3. 启动 "x64 Native Tools Command Prompt for VS 2022" 4. `cd <<你的 RemoteNET 仓库路径>>\src` 5. `mkdir detours_build` 6. `cd detours_build` 7. `cmake ..\detours.net` 8. `msbuild /t:restore,build ALL_BUILD.vcxproj` 9. 在 Visual Studio 中打开 `RemoteNET.sln` 10. 编译 RemoteNET 项目 ## 最小可行示例 这个有趣的示例转储了目标应用程序内存中所有的私有 RSA 密钥(存储在 `RSACryptoServiceProvider` 中): ``` Func ToHex = ba => BitConverter.ToString(ba).Replace("-", ""); // Finding every RSACryptoServiceProvider instance RemoteApp remoteApp = RemoteAppFactory.Connect("MyDotNetTarget.exe", RuntimeType.Managed); var rsaProviderCandidates = remoteApp.QueryInstances(typeof(RSACryptoServiceProvider)); foreach (CandidateObject candidateRsa in rsaProviderCandidates) { RemoteObject rsaProv = remoteApp.GetRemoteObject(candidateRsa); dynamic dynamicRsaProv = rsaProv.Dynamify(); // Calling remote `ExportParameters`. // First parameter (true) indicates we want the private key. Console.WriteLine(" * Key found:"); dynamic parameters = dynamicRsaProv.ExportParameters(true); Console.WriteLine("Modulus: " + ToHex(parameters.Modulus)); Console.WriteLine("Exponent: " + ToHex(parameters.Exponent)); Console.WriteLine("D: " + ToHex(parameters.D)); Console.WriteLine("P: " + ToHex(parameters.P)); Console.WriteLine("Q: " + ToHex(parameters.Q)); Console.WriteLine("DP: " + ToHex(parameters.DP)); Console.WriteLine("DQ: " + ToHex(parameters.DQ)); Console.WriteLine("InverseQ: " + ToHex(parameters.InverseQ)); } ``` 有关更高级的示例,[请参阅这个“KeeFarce”重制版](./KeeFarce_lookalike.md)。 ## 使用指南 本节记录了你可能需要的库 API 的大部分内容。 ### ✳️ 设置 要开始操作远程进程,你需要像这样创建一个 `RemoteApp` 对象: ``` // For .NET targets RemoteApp remoteApp = RemoteAppFactory.Connect("MyDotNetTarget.exe", RuntimeType.Managed); // For MSVC C++ target RemoteApp remoteApp = RemoteAppFactory.Connect("MyNativeTarget.exe", RuntimeType.Unmanaged); ``` 如果你有多个同名进程在运行, 你可以使用重载方法 `Connect(System.Diagnostics.Process p, RuntimeType r)`。 ### ✳️ 获取现有的远程对象 RemoteNET 首要的功能是允许你在远程应用程序中查找现有对象。 为此,你需要搜索远程堆。 使用 `RemoteApp.QueryInstances()` 查找所需对象的可能候选者,并使用 `RemoteApp.GetRemoteObject()` 获取候选者的句柄。 ``` IEnumerable candidates = remoteApp.QueryInstances("MyApp.PasswordContainer"); RemoteObject passwordContainer = remoteApp.GetRemoteObject(candidates.Single()); ``` ### ✳️ 创建新的远程对象 有时,远程应用程序中的现有对象不足以满足你的需求。 因此,你也可以远程创建新对象。 为此请使用类似 `Activator` 的接口: ``` // Creating a remote StringBuilder with default constructor RemoteObject remoteSb1 = remoteApp.Activator.CreateInstance(typeof(StringBuilder)); // Creating a remote StringBuilder with the "StringBuilder(string, int)" ctor RemoteObject remoteSb2 = remoteApp.Activator.CreateInstance(typeof(StringBuilder), "Hello", 100); ``` 注意我们在第二次 `CreateInstance` 调用中是如何使用构造函数参数的。这些参数也可以是其他的 `RemoteObject`: ``` // Constructing a new StringBuilder RemoteObject remoteStringBuilder = remoteApp.Activator.CreateInstance(typeof(StringBuilder)); // Constructing a new StringWriter using the "StringWriter(StringBuilder sb)" ctor RemoteObject remoteStringWriter = remoteApp.Activator.CreateInstance(typeof(StringWriter), remoteStringBuilder); ``` ### ✳️ 读取远程字段/属性 为了提供流畅的编码体验,RemoteNET 使用了一种特殊的动态对象,任何 `RemoteObject` 都可以转换为它。 该对象可以用来访问字段/属性,就像它们是本地对象的字段/属性一样: ``` // Reading the 'Capacity' property of a newly created StringBuilder RemoteObject remoteStringBuilder = remoteApp.Activator.CreateInstance(typeof(StringBuilder)); dynamic dynamicStringBuilder = remoteStringBuilder.Dynamify(); Console.WriteLine("Remote StringBuilder's Capacity: " + dynamicStringBuilder.Capacity) ``` 一个更有趣的示例是检索每个 `SqlConnection` 实例的 `ConnectionString`: ``` var sqlConCandidates = remoteApp.QueryInstances(typeof(SqlConnection)); foreach (CandidateObject candidate in sqlConCandidates) { RemoteObject remoteSqlConnection = remoteApp.GetRemoteObject(candidate); dynamic dynamicSqlConnection = remoteSqlConnection.Dynamify(); Console.WriteLine("ConnectionString: " + dynamicSqlConnection.ConnectionString); } ``` ### ✳️ 调用远程方法 就像访问字段一样,可以在动态对象上调用方法。 请参阅上面关于转储 RSA 密钥的示例。 ### ✳️ 远程事件 你也可以订阅/取消订阅远程事件。语法类似于“标准 C#”,尽管不完全相同: ``` CandidateObject cand = remoteApp.QueryInstances("System.IO.FileSystemWatcher").Single(); RemoteObject remoteFileSysWatcher = remoteApp.GetRemoteObject(cand); dynamic dynFileSysWatcher = remoteFileSysWatcher.Dynamify(); Action callback = (dynamic o, dynamic e) => Console.WriteLine("Event Invoked!"); dynFileSysWatcher.Changed += callback; /* ... Somewhere further ... */ dynFileSysWatcher.Changed -= callback; ``` 限制: 1. 回调的参数必须是 `dynamic` 2. 回调必须为该事件定义确切数量的参数 3. 不允许使用 Lambda 表达式。回调必须转换为 `Action<...>`。 ### ✳️ 注册自定义函数(仅限非托管/C++ 目标) 对于非托管(MSVC C++)目标,你可以注册未被 RTTI 扫描器自动发现的自定义函数。 当你知道目标进程中某个函数的地址并希望通过 RemoteNET 调用它时,这非常有用。 **重要:** 类型必须是从远程进程获取的 `RemoteRttiType` 实例。该类型的任何现有实例都将自动更新为新方法。 ``` // Connect to an unmanaged target UnmanagedRemoteApp unmanagedApp = (UnmanagedRemoteApp)RemoteAppFactory.Connect("MyNativeTarget.exe", RuntimeType.Unmanaged); // Get a remote type (this creates a RemoteRttiType) Type remoteType = unmanagedApp.GetRemoteType("MyNamespace::MyClass", "MyModule.dll"); // Register a custom function on the type - returns the MethodInfo or null on error MethodInfo customMethod = unmanagedApp.RegisterCustomFunction( parentType: remoteType, functionName: "MyCustomFunction", moduleName: "MyModule.dll", offset: 0x1234, // Offset from module base address returnType: typeof(int), parameterTypes: new[] { typeof(int), typeof(float) } ); if (customMethod != null) { // After registration, the function can be invoked like any other remote method // All existing instances of this type will have the new method available dynamic dynamicObj = remoteObject.Dynamify(); int result = dynamicObj.MyCustomFunction(42, 3.14f); } ``` **注意:** - 此功能仅适用于非托管(C++)目标 - 父类型必须是 RemoteRttiType(通过 `UnmanagedRemoteApp.GetRemoteType()` 获取) - 你需要知道函数所在的模块名称和偏移量 - 该函数将被添加到类型的方法列表中,并在所有实例上可用 - 参数和返回类型应指定为 .NET 类型 - 返回已注册方法的 `MethodInfo`,如果注册失败则返回 `null` ## 待办事项 1. 静态成员 2. 记录“Reflection API”(RemoteType, RemoteMethodInfo, ... ) 3. 支持其他 .NET framework CLR 版本(.NET 4 之前)。目前支持 v4.0.30319 4. 记录 Harmony(prefix/postfix/finalizer 钩子) 5. 支持更多 Harmony 功能 ## 致谢 [denandz/KeeFarce](https://github.com/denandz/KeeFarce):这是本项目的主要灵感来源。 此外,本项目的多个部分改编自 KeeFarce(DLL 注入、Bootstrap、IntPtr-to-Object 转换器)。 [TheLeftExit/Trickster](https://github.com/TheLeftExit/Trickster):我用它作为 MSVC Diver 的基础(针对 C++ 目标)。主要是堆中的 C++ 对象搜索。 [pardeike/Harmony](https://github.com/pardeike/Harmony):我用它来钩子 .NET 方法。 [microsoft/Detours](https://github.com/microsoft/Detours):我用它来钩子原生方法。 [citronneur/detours.net](https://github.com/citronneur/detours.net):我用它作为 Detours 的包装器。 [uxmal/reko](https://github.com/uxmal/reko):我用它来还原(demangle)C++ 符号。 **icons8** 提供的“Puppet”图标 **Raymond Chen** 在 [2010 年的这篇博文](https://devblogs.microsoft.com/oldnewthing/20100812-00/?p=13163)中声明这个项目不应该被实现。 我非常喜欢帖子中的这句话:
标签:DLL注入, Hook, MSVC, Obj hunt, RSA密钥提取, SSH蜜罐, 云资产清单, 内存操作, 反射, 多人体追踪, 恶意分析, 流量审计, 足迹探测, 运行时操作, 进程注入, 远程交互, 逆向工程