RemoteLib - DLL Injection for Win9x & NT Platforms

深入大模型-52-Harness综述之(二)四大子系统 一个Harness都由四个子系统拼成,Agentic Loop是主干,四个子系统是Tool系统、Memory & Context、Guardrails,再加上一个把它们优雅组织起来的Skill系统。 阅读详情

Motivation

This article was inspired by Robert Kuster's Three Ways to Inject Your Code into Another Process. At first, I was looking for some pieces of code that would allow me to easily inject my own DLLs into a remote running process. So I did a search on CP and found Robert Kuster's; it was really an excellent article I have to say, but unfortunately, none of those three solutions worked on my Windows 98 (Second Edition). In theory, the "first way" should work on all Windows platforms but it did not (that's why some programmers including me hate Win9x so much). The fix was actually very easy, but since Robert Kuster's code was rather a technical tutorial than an encapsulated reusable library, I thought why not write one for the ease of future uses.

Introduction

While DLL injection being extremely easy on NT platforms, Win9x users are out of luck because ::CreateRemoteThread is unsupported on Win9x. There are of course other solutions, ::SetThreadContext, for example, coupled with ::CreateProcess, can handle the job for you, but it'd be a bit difficult to inject code into an already-running process because suspending a running thread on Win9x is relatively complicated.

Another simple solution is using ::SetWindowsHookEx, as Robert Kuster explained in his article, thread-hooking (in contrast to global-hooking) a window will result the entire DLL (where the hook procedure resides) be mapped into the target window's creator process; this gives us an opportunity to do any dirty work inside the target process' virtual space, including but not limited to, calling ::LoadLibrary or ::FreeLibrary to map/unmap third-party DLLs into/from the target process, A.K.A., DLL injection.

The down-side of using Windows hooks is that this technique requires the remote process to have at least one valid window, which means you cannot inject your DLLs into window-less applications or services. And that's why I provided two sets of loading/unloading functions for Win9x and NT respectively; those for Win9x require the target process to have a window, whereas those for NT do not. If, however, you want to inject DLLs into running window-less processes on Win9x, this article may not be of help.

How Does It Work

The source code attached to this article is a Win32 DLL project. Compile it (or simply download the "DLL binary files" package) and you will get RemoteLib.dll. This DLL acts like an "intermedium" or "bridge" between the target process and your own DLL. It basically handles the code injection in the following way:

  1. RemoteLib.dll injects itself into the target process. Either by using hook or creating remote thread, depending on whether you are using Win9x or NT.
  2. If step 1 succeeds, RemoteLib.dll maps your DLL into the target process through a call to ::LoadLibraryA or ::LoadLibraryW, depending on whether UNICODE is defined.
  3. RemoteLib.dll unmaps itself from the target process.

To un-inject your code from the target process, step 2 is changed, RemoteLib.dll will call ::FreeLibrary instead.

Implementation

I'm not going to discuss the ::CreateRemoteThread approach in details since it won't work on Win9x at all, this article will explain more on Windows hooks.

First of all, since Win9x does not support ::VirtualAllocEx and ::VirtualFreeEx, we need to find a way to pass our DLL name string into the target process for subsequent calls to ::LoadLibrary and ::GetModulehandle, and the ::GetModuleFileName call will also require a string buffer in the remote process' virtual space to temporarily store the DLL name string. Luckily, we have a linker option to do this:

#pragma data_seg ("SHARED")
static HHOOK g_hHook = NULL; // Hook handle.
static wchar_t g_szDllPath[MAX_PATH + 1] = { 0 }; // DLL path.
// other shared data...
#pragma data_seg ()
#pragma comment(linker, "/section:SHARED,RWS")

Then, we need to choose the correct hook type, the most commonly used WH_CALLWNDPROC will not work on Win9x, as MSDN states:

Windows 95/98/ME, Windows NT 3.51: The WH_CALLWNDPROC hook is called in the context of the thread that calls SendMessage, not the thread that receives the message.

That's completely against the purpose of this project. So we must use another hook type instead. While there may be better choices, I personally found out that WH_CBT is by no means a bad one.

//////////////////////////////////////////////////////////////////
// Pseudo-code for injecting a DLL into a remote running process.
//////////////////////////////////////////////////////////////////

// First we save the DLL name in our shared string buffer.
::strncpy((LPSTR)g_szDllPath, "c://test//MyTest.dll", MAX_PATH); 

// Then RemoteLib.dll maps itself into the remote process through windows hook
g_hHook = ::SetWindowsHookEx(WH_CBT, (HOOKPROC)HookProcA, 
                    g_hModInstance, dwTargetWndThreadID);
if (g_hHook == NULL)
{
    // Error handling...
}

// Send a special system message to the target window
if (!::SendMessageTimeoutA(hTargetWnd, WM_SYSCOMMAND, 
       0, REMOTE_LOADLIBRARY, 
       SMTO_ABORTIFHUNG | SMTO_BLOCK, 2000, NULL))
{
    // Error handling...
}

if (g_hHook)
{
     // Make sure we remove the hook, in case it wasn't removed by "HookProc"
     ::UnhookWindowsHookEx(g_hHook);
     g_hHook = NULL;
}

// Send a dummy system message
// to the target window to force unloading RemoteLib.dll
::SendMessageTimeoutA(hTargetWnd, WM_SYSCOMMAND, 0, 0, 
               SMTO_ABORTIFHUNG | SMTO_BLOCK, 2000, NULL);

Above code will map RemoteLib.dll itself into the target process, and unmap after ::SendMessageTimeoutA returns. But what happens in between? Now we need to take a look at the hook procedure and see what it does.

///////////////////////////////////////////////////////////////////////
// HookProcA (Pseudo-code)
///////////////////////////////////////////////////////////////////////
LRESULT CALLBACK HookProcA(int code, WPARAM wParam, LPARAM lParam)
{ 
    if (code == HCBT_SYSCOMMAND && lParam == REMOTE_LOADLIBRARY) 
     {
        if (g_hHook)
        {
            // Remove the hook ASAP
            ::UnhookWindowsHookEx(g_hHook);
            g_hHook = NULL;
        }

        // Since we are now inside
        // the target process's virtual space already...
        g_dwProcResult = (DWORD)::LoadLibraryA((LPCSTR)g_szDllPath);
        g_dwProcError = ::GetLastError();

        return 1;
        // Returns 1 so the window won't receive this "meaningless" message
    }

    return ::CallNextHookEx(g_hHook, code, wParam, lParam);
}

So, what the hook procedure does is simple, because at that moment, the code was already executed inside the target process' virtual space. Calling ::LoadLibrary there just maps any given DLL into the target process, and we are done.

That's the main idea of this implementation. Of course, there are a lot more factors that need to be taken into account, for example:

  • Synchronization. Since we have declared shared data pieces, we must protect them from asynchronous access.
  • Retrieving return values and error codes produced by remote function calls.
  • Being UNICODE compliant.
  • Handling KERNEL32 functions that may not exist on some Windows platforms.
  • Verifying user Windows platform types (9x/NT) before invoking NT-only APIs.

I will leave all remaining details to the readers, please have a look at the project source code and see how those problems are solved.

Using the Code

To use the library, you need to:

  1. Copy RemoteLib.dll to your system directory or application directory.
  2. Add RemoteLib.lib into your project workspace.
  3. Include RemoteLib.h where needed.
  4. Call appropriate functions declared in RemoteLib.h.

For example, to inject/un-inject "c:/Tools/d2Hackit.dll" into Diablo2 game process, which always has a window whose class name is "Diablo II". Assuming that the user may be using Win9x, so this sample uses the Windows hook technique:

#include "RemoteLib.h"
#include <stdio.h>

void DisplayError(DWORD dwErrorCode)
{
    char szMsg[256] = "";
    sprintf(szMsg, "Function failed. Error code: %d", dwErrorCode);
    ::MessageBoxA(NULL, szMsg, "RemoteLib Error", MBOK);
}

int main()
{
    // Find the Diablo2 game window
    HWND hWnd = ::FindWindow("Diablo II", NULL);
    if (hWnd == NULL)
        return 1; // Game window not found.

    // Inject our DLL
    HMODULE hModule = RemoteLoadLibrary(hWnd, "c://Tools//D2Hackit.dll");

    if (hModule == NULL)
    {
        DisplayError(::GetLastError()); // Why did the function fail?
        return -1; // Injection failure.
    }

    /*
    DLL injected successfully. DO some stuff here...
    ... ...
    No we are going to un-inject the DLL
    */

    // If for some reason you
    // lost the previous hModule, you can have it back:
    // hModule = RemoteGetModuleHandle(hWnd, "c://Tools//D2Hackit.dll");

    // Un-inject our DLL
    BOOL bOK = RemoteFreeLibrary(hWnd, hModule);    

    if (!bOK)
    {
        DisplayError(::GetLastError()); 
        // Why did the function fail?

        return -2; // Un-injection failure.
    }

    return 0; // Every thing went fine... 
}

Conclusion

This library encapsulates all backend work for DLL injection, and provides a very simple interface to developers who want to inject code into other running processes. Functions exported by this library are all clearly self explained by their names, so I don't think I need to provide something like an "API reference", do I? It works for both Win9x and NT platforms, but please remember that on Win9x, it requires the remote process to have at least one valid window.

History

  • v1.00 Jan 03, 2005
    • Initial release.
  • v1.01 Jan 10, 2005
    • Modified RemoteGetModuleHandle and RemoteGetModuleHandleNT so you do not have to specify absolute DLL paths, instead, you now can specify relative paths or plain file names without paths, even without file extensions.
    • Reorganized the source code files to make them easier to understand.
    • Updated source/binary/demo packages.

=[ Abin ]=

机器学习面试笔试求职必背!八股文(1/5) 算法工程师,技术软件类求职机器学习必背八股文 更多算法/业务/HR面等笔试题面试题 —— &gt;个性签名自取! 1. 特征工程: 1.1 为什么要对特征做归一化处理: Feature scaling,常见的提法有“特征归一化”、“标准化”,是数据预处理中的重要技术。特征间的单位(尺度)可能不同,比如身高和体重,比如摄氏度和华氏度,比如房屋面积和房间数,一个特征的变化范围可能是[ 1000 , 10000 ] [1000, 10000][1000,10000],另一个特征的变化范围可能是[ −. 阅读详情

相关推荐

UDS诊断入门第一课:手把手教你玩转0x10会话控制服务(附ISO14229实战解析)

刚接触汽车电子诊断的工程师们,往往会被厚厚的ISO 14229标准文档和各种专业术语吓退。但就像学习编程要从"Hello World"开始一样,掌握UDS协议的最佳切入点就是0x10诊断会话控制服务。这个看似简单的服务,实际上是整个诊断系统的"钥匙"——它决定了ECU(电子控制单元)当前处于何种工作模式,直接影响着其他诊断服务的可用性。 在实际车辆诊断中,ECU默认处于DefaultSession模式,这种模式下许多高级诊断功能是被禁用的。想象一下你正在开发一个需要刷写ECU固件的工具,如果连最基本的会

weixin_30402343的博客 394

VC下提前注入进程的一些方法1——远线程不带参数

        前些天一直在研究Ring3层的提前注入问题。所谓提前注入,就是在程序代码逻辑还没执行前就注入,这样做一般用于Hook API。(转载请指明出处)自己写了个demo,在此记下。         我的demo使用了两种注入方式:1 远线程; 2 修改代码入口点。         先说下我代码风格,因为要处理很多异常逻辑,我比较喜欢do{}while(0);这样的结构,一旦出错,就b...

方亮的专栏 4034

深度解析NXP AMCLIB状态观测器:PMSM无感FOC控制实战指南

状态观测器是现代电机控制中的核心算法,它基于电机数学模型,通过测量定子电压和电流,实时估算出转子的位置和速度,从而替代了传统的物理传感器。其原理遵循“预测-校正”的闭环结构,通过比较模型预测值与实际采样值的误差,不断修正内部状态估计。这项技术的核心价值在于实现了无传感器控制,显著降低了系统成本、提升了可靠性与紧凑性,广泛应用于伺服驱动、变频器、电动汽车和家电等领域。本文聚焦于NXP AMCLIB库中的角度跟踪观测器和反电动势观测器,深入剖析其设计思路、固定点运算实现以及参数整定方法,为工程师在永磁同步电机无

cumi7754的博客 2833

Python+Agent入门笔记(六):记忆和状态,从“一次性问答”到“能用助手”

滑动窗口的缺点很明显:被扔掉的信息就真没了。如果用户第一轮说“我不吃辣”,隔了二十轮让他推荐餐厅,模型已经忘了。定期把旧的对话压缩成一段总结,作为一条特殊的消息插进去。"""把除 system 和最近 keep_recent 条以外的消息,压缩成一条摘要。返回新的 messages 列表。"""# 把旧消息拼成纯文本,让模型总结content = f"[工具结果]把除 system 和最近 keep_recent 条以外的消息,压缩成一条摘要。返回新的 messages 列表。

qq_41241612的博客 106

从零开始:用YOLOv8-Pose实现实时人体姿态检测(附完整代码)

本文提供了一份从零开始的完整实战指南,详细介绍了如何使用YOLOv8-Pose构建实时人体姿态检测系统。内容涵盖环境搭建、数据准备、模型训练与调优,以及高性能部署和进阶应用开发,并附有可直接运行的代码示例,帮助开发者快速掌握Pose Estimation技术并将其应用于实际项目。

h6i7j8的博客 622

MySQL安装配置教程(超级详细)

一、 下载MySQL Mysql官网下载地址:MySQL :: Download MySQL Installer (Archived Versions) 1. 选择要安装的版本,本篇文章选择的是5.7.31版本,点击Download下载 正在上传…重新上传取消 二、 安装MySQL 1. 选择设置类型双击运行mysql-installer-community-5.7.31.0.msi 这里选择是自定义安装,所以直接选择“Custom”,点击“Next” “Developer Default”是

m0_67322837的博客 5万+

循环嵌套(多层 for 循环)

表:order(订单表,order_id, user_id)、user(用户表,user_id, user_name)✅ 优化:先查所有订单,拿到所有 user_id 批量 in 查询,内存 join。✅ 优化:先把优惠券转 HashMap,单层循环查找(hash 查找 O (1))SQL 只有 2 次,O (1) 数据库查询,内存遍历 O (n)。需求:查询所有订单列表,同时带出每个订单对应的用户名称。需求:订单列表、优惠券列表,把订单匹配对应的优惠券名称。复杂度:O (M+N),非常快。

weixin_43732943的博客 214

保姆级教程:在Ubuntu 20.04上从零搞定ROS Noetic和RealSense驱动,为无人机开发铺路

本文提供了一份详细的Ubuntu 20.04环境下ROS Noetic和RealSense驱动安装指南,专为无人机开发环境搭建设计。从系统配置、ROS安装到RealSense驱动集成,涵盖所有关键步骤和常见问题解决方案,帮助开发者快速搭建自主空中机器人开发平台。

weixin_30329623的博客 65

本地chatbot4--引入LangGraph

上一节实现了chatbot的连续调用工具的功能,但目前流程还是提示词→模型→解析→调用工具(可选)→输出线性的一条路走到底(LangChain)只适合简单线性任务(问答、摘要、简单RAG)agent的本质是:“模型决定调工具→执行→结果喂回模型→再决定……直到完成”。举例:读日志 → 解析 ─失败→ 重试(≤3次) ─→ 退出│成功↓算统计 → 对比历史 → 生成报告 → [人工审核卡点] → 发送。

weixin_45859446的博客 102

GD32F303 ADC+DMA实战:用硬件过采样搞定多通道数据采集(附完整代码)

本文详细介绍了GD32F303系列MCU通过ADC+DMA结合硬件过采样技术实现多通道数据采集的实战方案。从硬件设计要点到软件配置全解析,包括时钟树配置、ADC模块初始化和DMA传输设置,提供了完整的代码示例和优化技巧,帮助工程师构建高精度、低功耗的数据采集系统。

weixin_30466039的博客 322

网络基础(二):数制转换与分类编址的IPv4地址

本文介绍数制转换、IP地址、分类编址的IPv4地址,希望对你有帮助!

十七拾的博客 1057

【数据结构-5】单链表的实现

单链表,具体来说是单向的、不带头的、不循环的链表。链表由一个一个节点组成,每个节点分为数据域和指针域,数据域存放数据,指针域存放下一节点的地址。这样,就可以根据指针域中的地址找到下一个节点,节点就串起来了构成了单链表。

a bit 215

Qt垂直滚动条QVerticalScrollBar详解:从基础到实战的控件开发指南

本文系统解析Qt中QVerticalScrollBar的用法,从基础概念到进阶技巧。主要内容包括:1)基础认知:介绍其继承关系、作用及典型应用场景;2)核心用法:创建方法、关键属性配置和信号槽连接;3)进阶技巧:自定义样式表美化外观,以及与自定义内容区域的联动实现;4)实战注意事项,如避免循环触发和高DPI适配等。文章强调掌握QVerticalScrollBar对提升Qt滚动类功能开发效率的重要性,并建议通过自定义文本编辑器实践巩固知识点。

专注现代C++实战,从核心语法到高性能编程,助力开发者深入掌握系统级编程精髓。 759

C++ 入门笔记(十七):list 入门 —— 为什么 list 不能 sort?

第17课:用大白话讲清 list 的双向链表结构、哨兵头、push_back 四步、迭代器封装、merge 与 splice 的区别,以及 vector 排序为什么常比 list 快。

wo_jack的博客 248

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

C# 数据结构和特殊类型

.NET Core(现称.NET)提供丰富数据结构,优化性能与开发效率。日常开发首选List&lt;T&gt;和Dictionary&lt;TKey,TValue&gt;;高性能场景推荐Span&lt;T&gt;与Memory&lt;T&gt;减少内存分配;并发环境使用ConcurrentDictionary等线程安全集合;避免非泛型集合如ArrayList。

cccloveforever的博客 744

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

list

的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。,因此反向迭代器的实现可以借助正向迭代器,即:反向迭代器内部可以包含一个正向迭代器,对正向迭代器的接口进行包装即可。是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。迭代到该位置,在这段位置上迭代需要线性的时间开销;最大的缺陷是不支持任意位置的随机访问,比如:要访问。还需要一些额外的空间,以保存每个节点的相关联信息。个元素,必须从已知的位置。

xie0510_的博客 291

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

3.链表相关知识点

链表

2501_93884468的博客 432

上万数据的批量插入

当数据达到上万上十万的时候,如何批量插入

junxunfs的博客 45

软件商店常见问题标准处理流程

从底层软件源来看,/etc/apt/sources.list配置错误、源地址失效、公网与内网离线源混用、ARM64与x86_64架构源串用,以及DNS解析异常、系统代理配置错误,都会导致商店无法获取应用数据;打开该应用,确认可正常运行;软件商店作为系统内置的图形化软件分发与安装入口(UKUI桌面配套组件,底层依赖APT软件源),承担着应用软件的浏览、搜索、下载、安装、升级与卸载等核心功能,是终端软件部署与管理的关键通道。覆盖软件商店启动、页面加载、应用下载、安装、卸载、搜索、自身更新等全部业务场景。

qq_39496637的博客 306

【AI 开发从入门到进阶 · 10】收官篇:LoRA 微调、模型评估、推理加速与生产部署全链路

本文系统梳理了大模型微调与生产落地的全流程,从决策是否微调、优选LoRA技术方案,到代码实现、效果评估、推理加速与FastAPI服务封装,最终构建可迭代的生产级AI系统。强调“逐级升级”原则,倡导以数据闭环驱动优化,避免技术冲动。全系列贯穿工程化思维,提供可复用模板与实战建议,助力开发者从模型训练迈向稳定可用的AI服务部署。

信息安全领域优质创作者|操作系统领域专家 126

LangGraph(03):状态归约、并行更新与消息管理

并行节点写入报错?历史消息无法清理?本文拆解 LangGraph 状态归约与消息增删改机制,带你精确控制复杂工作流状态。

专注于 Agent 工程、AI 产品、Harness Engineering、LLM 算法等方向,OpenMathModel、Nivik 等开源项目作者 140

ARIA规范

ARIA用来补充原生HTML缺失的语义,原则:优先原生标签,ARIA仅做补充。 ARIA角色role:定义组件身份,分为地标角色、构件角色、文档角色;构件角色用于自定义弹窗、tab、下拉菜单等复杂组件; ARIA状态与属性:以aria-开头,描述组件实时状态;常用:aria-label、aria-labelledby、aria-describedby、aria-hidden、aria-expanded、aria-selected、aria-modal、aria-live; 常用场景:图标按钮用aria-l

2401_83227843的博客 464

LangGraph 的分离思想总结

LangGraph 的分离思想,本质上是对"高内聚、低耦合"这一经典工程原则在 LLM 应用领域的落地。它通过状态与逻辑分离、节点职责分离、控制流与执行分离、计算与持久化分离、编排与实现分离五个层面,把一个难以维护的"大黑盒"拆解成一组可组合、可测试、可观测的部件。理解这套分离思想,比记住 API 更重要——因为当你面对新的智能体场景时,真正指导你做出合理拆分的,正是这些分离原则。

张晨光老师的播客 412

Java基础:泛型、类型擦除与 PECS 原则

泛型允许类、接口和方法使用类型参数,把许多类型检查提前到编译期,减少显式类型转换,提高代码复用性。例如。

cjpabc的博客 463

To-Do List 的「修改」功能与 CSS 定位

买牛奶</td><td index="1">写作业</td><td index="2">跑步

2401_89953644的博客 312

大模型记忆管理:消息是如何存储的

本文介绍了多轮对话中消息的存储方式,重点讲解了基于InMemoryChatMessageHistory的内存存储实现及会话隔离机制。通过为每个会话分配唯一ID,确保消息独立存储。同时展示了如何结合ChatPromptTemplate与消息历史实现上下文感知的对话,并提供了基于文件持久化的FileChatMessageHistory方案,支持消息长期保存与跨会话管理。

2301_80002260的博客 196
上一篇: 挂接CreateProcessW实现对进程创建的完全控制
下一篇: Three Ways to Inject Your Code into Another Process
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值