将动态连接库注入到其他进程中的一种新方法

本文介绍将动态连接库注入其他进程的新方法。常见注入方法有钩子函数和CreateRemoteThread函数,但各有局限。新方法思路类似CreateRemoteThread函数,可用于多种操作系统。文中详细阐述将DLL注入Explorer.exe进程的编程步骤,还给出源代码获取方式。

我们知道将动态连接库注入到其他进程中有很多种方法。最常见的方法是使用钩子函数(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 的实现也是很容易和规范的,我们可以用SuspendThreadGetThreadContextSDK函数轻松完成。

 

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

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

ThreadContext.Eip = (DWORD)m_lpCodeBase;

SetThreadContext(m_hInjectThread,&ThreadContext)

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

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

在我们写的装载DLL函数(InjectCodeFun)中还有两个问题我们需要解释一下,第一 在这个函数中我们不能使用任何我们自己程序中定义的变量,道理跟上面讲的一样,因为地址空间不同。还有我们不能直接调用函数,例如在InjectCodeFun中直接使用LoadLibray。这是因为如果直接使用LoadLibray那么就需要经过程序的Import表,跳转一下才能到达真正的WindowsLoadLibray函数。但是不同的进程有不同的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)
转载请征得作者同意.

 

<think>嗯,用户现在想了解在Linux系统中将.so文件注入到其他进程的方法,以及相关的开源工具。首先,我需要回顾一下之前提供的关于Android inline hook工具的信息,但用户这次的问题是关于Linux的,所以需要调整方向。 用户之前的问题是关于Android的ARM64 inline hook工具,我推荐了And64InlineHook、Dobby等。但现在转向Linux,我需要确保回答专注于Linux环境下的注入方法,同时参考用户提供的引用内容。 根据用户提供的引用,引用[1]提到了Linux Injector项目,引用[2]是HookSo工具,引用[3]是关于查找进程ID和ptrace_attach的方法,引用[4]是so文件加壳,引用[5]是Android的so注入。虽然引用[5]是Android相关,但可能某些方法在Linux上也有类似应用,但需要区分开来。 首先,用户需要的是Linux下的方法,所以应优先考虑引用[1]和[2]提到的工具:Linux Injector和HookSo。此外,可能还有其他开源工具如Frida或LD_PRELOAD技巧,虽然用户没有直接提到,但作为常见方法应该包括在内。 接下来,我需要整理这些工具的使用步骤和特点。例如,Linux Injector的使用步骤可能包括编译安装、查找PID、注入.so。而HookSo可能提供更动态的修改功能。ptrace方法也是一个基础,需要解释如何通过代码实现注入,比如使用ptrace附加进程,调用dlopen加载.so。 另外,用户可能对技术原理感兴趣,比如如何通过覆盖函数指针或修改GOT/PLT来实现注入。引用[3]提到的/proc/pid/cmdline查找进程ID的方法,应该包含在步骤中。同时,引用[4]提到的so加壳可能涉及注入后的保护措施,但用户主要问的是注入方法,可能作为注意事项提及。 需要注意区分动态链接注入的不同方法,比如动态链接注入(LD_PRELOAD)、ptrace手动注入、工具自动化注入等。同时,要提到权限问题,如需要root权限或调试权限,以及如何通过mprotect修改内存保护属性。 可能用户还想了解如何验证注入是否成功,比如检查进程的内存映射或日志输出。此外,引用[5]中的constructor属性在Linux下同样适用,可以在.so的初始化函数中执行代码,无需显式调用。 最后,确保回答结构清晰,分步骤介绍方法,推荐工具,并附上注意事项和常见问题。需要检查是否有遗漏的开源工具,比如Frida虽然主要用于动态插桩,但也支持Linux进程注入,应该包括在内。</think>### Linux 系统中动态库(.so)注入方法与开源工具解析 在 Linux 系统中,动态库注入(SO Injection)是调试、性能分析、安全研究等领域的关键技术。以下是结合开源工具与底层原理的 **5种主流注入方法**,涵盖从基础到高阶的实现方案: --- ### 一、开源工具注入法(推荐) #### 1. **Linux Injector** - **项目地址**: [GitHub - Linux-Injector](https://github.com/gaffe23/linux-inject) - **核心原理**: 通过 `ptrace` 系统调用劫持目标进程执行流,强制加载指定 .so 文件 - **使用步骤**: 1. 编译工具:`make` 2. 查找目标进程 PID:`ps -aux | grep <进程名>` 3. 注入命令:`./inject -p <PID> -l /path/to/libexample.so` - **技术优势**: - 支持 **x86/x64/ARM** 架构 - 自动处理 `dlopen` 调用与符号解析 - 提供 **线程暂停/恢复** 保护机制[^1] #### 2. **HookSo** - **项目地址**: [GitHub - HookSo](https://gitcode.com/gh_mirrors/ho/hookso) - **核心功能**: - 动态链接库 **注入/修改/查询** 三合一工具 - 支持 **GOT/PLT Hook** 与 **inline hook** - **关键操作**: ```bash # 注入.so到进程 ./hookso inject -p <PID> -s libinject.so # 列出已注入模块 ./hookso list -p <PID> ``` - **特色**: - 提供 **内存读写监控** 功能 - 内置 **ELF 解析引擎**,支持符号重定向[^2] --- ### 二、手动编码实现法(进阶) #### 3. **ptrace + dlopen 手动注入** - **实现步骤**: 1. **附加进程**:`ptrace(PTRACE_ATTACH, pid, NULL, NULL)` 2. **获取寄存器状态**:`ptrace(PTRACE_GETREGS, ...)` 3. **调用 `dlopen`**: - 通过 `mmap` 在目标进程分配内存 - 写入 `/path/to/lib.so` 路径字符串 - 调用 `dlopen` 加载动态库 4. **恢复执行**:`ptrace(PTRACE_DETACH, ...)` - **代码片段**: ```c void *remote_addr = mmap_remote(pid, sizeof(path)); write_remote(pid, remote_addr, path, sizeof(path)); call_remote(pid, (void*)dlopen, remote_addr, RTLD_NOW); ``` - **技术难点**: - 需要精确处理 **寄存器保存/恢复** - 需绕过 **ASLR**(地址空间布局随机化)[^3] --- ### 三、动态链接器技巧 #### 4. **LD_PRELOAD 环境变量注入** - **原理**:利用动态链接器优先加载 `LD_PRELOAD` 指定的 .so - **操作命令**: ```bash # 启动时注入 LD_PRELOAD=/path/to/libhook.so ./target_program # 运行时注入(需配合进程挂起) gdb -p <PID> -ex 'call __libc_dlopen("/path/to/libhook.so")' -ex 'detach' ``` - **限制**: - 仅适用于 **新启动进程** - 需目标程序未设置 `NO_BIND_NOW` 标志[^5] --- ### 四、内核模块注入法(高风险) #### 5. **SystemTap 动态注入** - **工具定位**:内核级动态追踪框架 - **注入脚本示例**: ```c probe process("<目标进程名>").function("main") { dlopen("/path/to/libinject.so", RTLD_LAZY); } ``` - **执行命令**:`stap -g inject.stp` - **优势**: - 绕过用户态权限限制 - 支持 **无暂停注入** --- ### 技术对比表 | 方法 | 侵入性 | 权限需求 | 适用范围 | 稳定性 | |--------------------|--------|------------|----------------|---------| | Linux Injector | 低 | 普通用户 | 运行中进程 | ★★★★☆ | | HookSo | 中 | root | 动态修改链接库 | ★★★★☆ | | ptrace 手动注入 | 高 | ptrace权限 | 调试场景 | ★★☆☆☆ | | LD_PRELOAD | 低 | 进程控制权 | 新启动进程 | ★★★★★ | | SystemTap | 极高 | root | 内核级操作 | ★★★☆☆ | --- ### 实践注意事项 1. **权限控制**: - `ptrace` 操作需关闭 `/proc/sys/kernel/yama/ptrace_scope` 或拥有 `CAP_SYS_PTRACE` 能力 - 注入后的 .so 需与目标进程 **ABI 兼容** 2. **反检测策略**: - 使用 **so加壳** 隐藏注入痕迹(如 XOR 加密 .so 文件头)[^4] - 通过 `mprotect` 修改内存页属性为 `PROT_READ|PROT_EXEC` 3. **调试技巧**: - 检查 `/proc/<PID>/maps` 确认 .so 加载状态 - 使用 `strace -f -p <PID>` 跟踪系统调用 --- ### 开源工具扩展推荐 - **Frida**:跨平台动态插桩框架,支持 JavaScript 脚本化注入 - **DynInst**:二进制重写库,提供 API 级注入控制 - **GDB Script**:通过自动化脚本实现断点触发式注入 如果需要具体某个工具的详细使用教程或代码示例,可进一步说明需求。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值