进程注入相关问题


1.为什么要把dll注入到某些进程中?

将DLL(动态链接库)注入到另一个进程中,通常是为了扩展或修改目标进程的行为,而无需修改其原始代码。这种技术在多种场景中都有应用:

DLL注入是一种强大的技术,不当的DLL注入可能导致程序不稳定甚至系统崩溃。

  1. 增强功能:通过向应用程序注入自定义的DLL,可以为其添加原本不具有的新功能。例如,某些第三方插件系统就是基于这种方式工作的。
  2. 调试和逆向工程:开发者可以通过注入DLL来监视、分析或调试一个正在运行的应用程序。这对于了解软件的工作原理或者查找并修复错误非常有用。
  3. 自动化操作:一些自动化工具可能需要直接与应用程序内部交互,这时可以通过注入DLL的方式来实现所需的操作。
  4. 安全软件:防病毒软件和防火墙可能会使用DLL注入技术来监控特定进程的行为,以检测潜在的安全威胁。
  5. 破解软件:利用DLL注入来篡改软件行为,比如绕过授权验证等。

2.进程注入有哪些方式?

dll注入是指将一个动态库加载到另一个进程的地址空间中,从而达到扩展或修改该进程的目的,

注入方式如下:

  1. CreateRemoteThread:最常用的动态库注入方式,通过在目标进程中创建线程来调用LoadLibrary函数,从而加载指定的动态库。
  2. SetWindowsHookEx:利用windows的钩子机制进行动态库注入,常用于拦截和处理特定类型的windows消息。
  3. AppInit_DLLs:通过修改注册表键 KEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs 可以指定随用户32位应用程序一起加载的dll,64位系统需要修改相应的64位注册表路径。
  4. 内存映射文件:通过共享内存的方式让dll动态库代码被目标进程执行。
  5. 远程进程的PEB(Process Environment Block)操作:直接操作目标进程的环境块来加载dll(复杂且不稳定)

注入的主要步骤:

  1. 打开目标进程:使用OpenProcess函数获取目标进程的句柄,需要有适当的权限才能成功执行这一步。
  2. 分配内存:使用 VirtualAllocEx 函数为目标进程分配足够的内存空间,用于存储DLL的路径名称。
  3. 写入DLL路径:利用 WriteProcessMemory 函数将DLL的完整路径名写入到之前分配的内存空间中。
  4. 获取LoadLibrary地址:找到kernel32.dll中的LoadLibraryA(或LoadLibraryW)函数的地址,这个函数负责加载DLL。
  5. 创建远程线程:使用CreateRemoteThread函数,在目标进程中创建一个新线程,并将LoadLibrary的地址作为线程入口点,同时传递前面写入的DLL路径地址作为参数。
  6. 等待加载完成:可选择等待远程线程完成,即等待DLL加载完毕。
  7. 清理资源:如果不需要了,记得释放分配的内存和关闭打开的进程句柄等。

3.进程如何防止被注入?

反注入,即防止其他进程或恶意软件将dll注入到自己的地址空间中,是一个复杂的过程。

因为windows操作系统本身提供了多种机制允许动态库加载,这同时也为注入提供了途径,常见的防御措施和技术来防止DLL注入:

  1. 代码完整性检查:
    • 定期检查自身的内存空间,确保没有未授权的代码被插入。如果发现异常,可以采取措施如重启进程、记录日志等。
  2. 保护进程的内存空间:
    • 使用VirtualProtectEx函数将关键内存区域设置为不可写(只读),从而增加DLL注入的难度。
    • 设置DEP(数据执行保护)和ASLR(地址空间布局随机化),这些技术可以帮助减少攻击面。
  3. 监控模块加载:
    • 利用API Hooking技术拦截对LoadLibrary系列函数(如LoadLibraryA, LoadLibraryW, LoadLibraryExA, LoadLibraryExW)的调用,验证即将加载的DLL是否经过授权。
    • 注册并实现AppDomain.CurrentDomain.AssemblyResolve事件处理程序(针对.NET应用程序),控制外部DLL的加载。
  4. 增强权限管理:
    • 确保应用以最低权限运行,减少潜在攻击者利用漏洞进行DLL注入的机会。
    • 对于敏感操作考虑使用更严格的安全模型,比如Windows服务在较低权限下运行。
  5. 使用安全开发实践:
    • 遵循安全编码指南,避免缓冲区溢出等常见漏洞,这些漏洞可能被用来作为注入点。
    • 进行定期的安全审计和渗透测试,识别并修复可能存在的安全隐患。
  6. 利用操作系统特性:
    • Windows 10引入了受保护的进程功能,能够极大地限制其他进程对自身进程空间的操作,包括DLL注入。
    • 对于驱动级保护,可以探索微软的Protected Process Light (PPL) 或 Protected Process (PP)机制。

需要注意,没有任何方法能提供绝对的安全保证。攻击者总是寻找新的方法绕过现有的防护措施。因此保持系统和应用程序更新,及时修补已知漏洞是至关重要的。此外结合使用上述多种策略可以显著提高防护效果。

4.进程和镜像的区别?

在 Windows 内核中,进程Process 和 镜像Image 是两个非常相关但完全不同的概念,理解它们的区别对于理解 DLL 注入、驱动开发等底层工作非常关键。

进程:是指一个正在运行的程序实例,代表着独立的执行环境,其拥有自己的:虚拟地址空间、句柄表、安全上下文、多个线程

当调用 CreateProcess() 或类似 API 时,系统会执行以下步骤:

  1. 创建进程对象(EPROCESS)

  2. 创建初始线程(ETHREAD)

  3. 开始加载可执行文件到内存中(但此时不一定完全执行)

  4. 触发进程创建通知回调(驱动层可注册)

    驱动可以通过 PsSetCreateProcessNotifyRoutine 注册回调函数,在每次有进程被创建或销毁时收到通知

镜像:镜像是指可执行文件或dll动态库,被加载到内存中的映射数据结构。其包括 .text代码段、.data数据段、.reloc重定位表、rdata只读数据

  • 镜像 = 文件在磁盘上的内容 + 被加载到进程地址空间中

每当系统加载一个模块.exe \ .dll时,会触发镜像加载回调,这是由 PsSetLoadImageNotifyRoutine 注册的驱动来响应的。其可以监控:

  1. 主exe加载
  2. 系统dll加载(如ntdll.dll、kernal32.dll)
  3. 自定义dll加载
属性进程镜像
对应结构体EPROCESS无单独结构,通常为 LDR_DATA_TABLE_ENTRY 列表节点
触发通知PsSetCreateProcessNotifyRoutinePsSetLoadImageNotifyRoutine
生命周期从进程创建到退出每个模块加载到进程地址空间中
关注的内容进程ID、权限、句柄表、地址空间等加载模块的路径、加载地址、是否系统DLL等
是否可注入点是:设置全局状态,准备注入等是:DLL加载稳定后触发注入(如kernel32加载时)
多次出现可能性每个PID唯一一个进程中可加载多个镜像

5.常见的CPU架构/指令集

系统架构(System Architecture)= 操作系统(OS)+ CPU架构(ISA),其定义了程序在哪个系统和cpu上能够运行 以及如何运行。

  1. 硬件平台架构:x86 / x64 / ARM 等
  2. 操作系统架构:32 位或 64 位
  3. 用户程序架构:是否运行在兼容层,如 Wow64、QEMU

常见cpu指令集架构:Instruction Set Architecture, ISA

架构名称位数类型代表CPU特点
x8632-bitCISCIntel/AMD 早期处理器指令复杂、向后兼容 DOS/Win9x
x64/AMD6464-bitCISCIntel Core, Ryzen 等向下兼容 x86,更强大的寄存器与寻址
ARMv732-bitRISC老Android 手机、树莓派节能,但能力有限
ARM64/AArch6464-bitRISC手机/嵌入式设备/Apple M 芯片高能效低功耗、精简指令集、适合移动场景

Windows常见系统架构

硬件架构操作系统架构应用程序场景
x8632 位 Windows32 位程序传统老系统(WinXP、Win7-32)
x64/AMD6464 位 Windows64 位程序主流 PC 系统(Win10/11)
x64/AMD6464 位 Windows32 位程序通过 Wow64 仿真运行
ARM64Windows on ARMARM64 程序原生 ARM 架构应用(如 UWP)
ARM64Windows on ARMx86/x64 程序借助仿真器(ARM64EC、x64 仿真)
  1. Windows x64 系统能通过 WOW64 子系统 运行 x86 应用。
  2. Windows ARM64 系统(如Surface Pro X)支持运行 ARM64 原生程序 + x86/x64 模拟程序。
  3. 注入dll\驱动时必须与目标的应用程序架构一致。

Linux常见系统架构

硬件架构支持情况常见发行版场景
x86已经式微Debian 32-bit、旧版 Ubuntu老PC、虚拟机
x64/AMD64主流架构Ubuntu, Fedora, Arch, CentOS主流PC、服务器
ARMv7轻量支持Raspbian, Ubuntu Mate树莓派、老Android 手机
ARM64/AArch64强力支持Ubuntu ARM64, Fedora ARM, Alpine ARM树莓派4/5、安卓盒子、M1/M2 Mac、新手机、嵌入设备
  1. Linux 借助 QEMU 可在不同平台上 跨架构 运行程序,例如在 x64 上运行 ARM 程序。
  2. ARM 平台(如树莓派)可以运行混合 32/64 位用户程序(如 ARM64 核心 + ARMv7 程序)

6.为什么会有x86/x64/ARM64的划分?

因为计算机的硬件(CPU)只能执行特定格式的机器指令,这些格式由指令集(ISA)决定,

架构之分源于CPU的指令集差异,不同架构的程序机器码完全不同,不可通用。程序必须匹配操作系统和硬件架构,除非通过兼容层或仿真器。

不同的指令集ISA的不同点:

  1. 指令长度不同
  2. 寄存器数量和用途不同
  3. 调用约定、内存模型不同
  4. 汇编语言和机器码完全不同

所以由于cpu被设计使用某种指令集ISA(x86、x64、arm64),所以操作系统内核必须要能够执行该架构指令(调度进程、管理内存),最后应用程序必须编译为对应架构的机器码,最终对应架构的应用程序才能在,对应架构的操作系统上运行、被对应架构的cpu执行。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

7.应用程序为什么要匹配对应的cpu架构?

如果将一段编译为 ARM64 的程序,放在一台 x64 架构的机器上运行,会发生什么?

答:直接崩溃(非法指令)。因为:

  • ARM64 的二进制指令完全无法被 x64 CPU 理解;
  • 操作系统也无法加载该架构格式的 ELF/PE 可执行文件;
  • CPU 会抛出 “非法指令”(Invalid Opcode)。

因此必须有:

  • 与 CPU 架构匹配的 操作系统
  • 操作系统内部提供该架构的 加载器、调度器
  • 与系统架构一致的 应用程序

8.同一份程序如何交叉编译成多个架构?

交叉编译是指在一种架构上(如 x64 PC),生成另一种架构(ARM、MIPS)的可执行文件的过程。

借助交叉编译器(cross-compiler)可以实现应用程序的交叉编译:

如主机架构:x86_64进行交叉编译:

  1. aarch64-linux-gnu-gcc:生成arm64程序
  2. arm-linux-gnueabihf-gcc:生成arm32程序
  3. x86_64-w64-mingw32-gcc:生成Windows x64程序(从Linux)
  4. riscv64-linux-gnu-gcc:生成RISC-V程序
# 主要步骤
# 安装交叉编译工具链
# 交叉编译
# 检查目标架构
# 输出: ELF 64-bit LSB executable, ARM aarch64 ...
# 输出: PE32+ executable (console) x86-64 ...
# 1.Linux C 程序交叉编译为 ARM64
sudo apt install gcc-aarch64-linux-gnu
aarch64-linux-gnu-gcc hello.c -o hello_arm64
file hello_arm64
# 2.Linux 下交叉编译 Windows 程序
sudo apt install mingw-w64
x86_64-w64-mingw32-gcc hello.c -o hello.exe
file hello.exe

或者使用cmake支持多架构交叉编译:

mkdir build_arm64 && cd build_arm64
cmake .. \
  -DCMAKE_SYSTEM_NAME=Linux \
  -DCMAKE_SYSTEM_PROCESSOR=aarch64 \
  -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \
  -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++
make

9.驱动/注入如何针对不同系统架构适配?

驱动或注入类程序在不同系统架构上运行时,必须适配对应的体系结构差异,这是系统级开发关键的一环,与操作系统最紧耦合的工作。

  1. 驱动适配:INF支持多平台,使用条件编译,针对不同内核结构处理
  2. 注入适配:区分 x86/x64/WOW64,准备对应架构的 shellcode/DLL
  3. 编译适配:使用交叉编译或多目标编译,准备多版本的驱动/注入代码
  4. 接口适配:系统调用号、结构体偏移、PEB/TEB 等都可能架构相关
  5. 流程适配:注入流程(如 APC/RemoteThread)在不同架构下有差异
驱动适配
  1. 编译平台和目标架构区分:

    • 用WDK做驱动开发时需要配置 TargetPlatformVersionTargetArchitecture

      <Configuration>
        <TargetPlatformVersion>10.0.19041.0</TargetPlatformVersion>
        <TargetArchitecture>x64</TargetArchitecture>
      </Configuration>
      
    • 编译多个架构版本的驱动(x86.sys、x64.sys、arm64.sys)

    • 使用 INF 文件中设置架构区段:

  2. 条件编译与架构差异处理

    #ifdef _M_X64
        // 64位专属处理,比如 EPROCESS 偏移、系统调用结构体等
    #elif defined(_M_IX86)
        // 32位处理
    #elif defined(_M_ARM64)
        // ARM64 架构处理(Windows 11 ARM 平台)
    #endif
    
  3. 不同架构的内核结构体不一样:

    • EPROCESSETHREADPEBTEB 结构体字段在不同架构偏移不同
    • 不同架构有不同内核线程/地址空间实现(如 ARM 没有 segmentation)
    • 用调试器获取对应字段偏移(WinDbg、ReClass、Ghidra)
    • 构建多个版本或动态获取偏移(如使用符号解析)
注入适配
  1. 区分目标进程架构
  2. 注入代码必须与目标进程架构一致
    • 64 位进程:只能注入 64 位 DLL(不能注入 32 位 DLL)
    • 32 位进程:只能注入 32 位 DLL(不能注入 64 位 DLL)
    • WOW64 的注入方式与普通进程略有不同(需要特殊处理)
    • Windows ARM 支持:
      • 64 位 ARM 应用 -> 注入 ARM64 DLL
      • 32 位 ARM 应用 -> 注入 ARM32 DLL
      • 通过 x86 仿真支持 32 位 x86 应用(WOW64 仿真)-> 注入 x86 DLL

10.驱动分层

驱动分层win

Windows 采用 WDM(Windows Driver Model) 作为驱动程序的基础框架,其核心分层结构如下:

┌───────────────────────────────────────────────┐
│                用户模式驱动                   	│
│  (WDF UMDF、WMI、性能计数器、打印驱动等)         	│
├───────────────────────────────────────────────┤
│                内核模式驱动                   	│
│  ┌───────────────────────────────────────────┐ │
│  │              过滤驱动层                    	│ │
│  │  (安全过滤、加密过滤、性能监控过滤等)        	│ │
│  ├───────────────────────────────────────────┤ │
│  │              功能驱动层                    	│ │
│  │  (实现设备核心功能,如网卡驱动、声卡驱动)      	│ │
│  ├───────────────────────────────────────────┤ │
│  │              总线驱动层                    │ │
│  │  (管理设备连接,如PCIe总线、USB总线)          │ │
│  └───────────────────────────────────────────┘ │
├───────────────────────────────────────────────┤
│                  硬件抽象层 (HAL)              │
│  (屏蔽不同硬件平台差异,如x86、ARM、服务器平台)   │
├───────────────────────────────────────────────┤
│                     硬件                        │
│  (CPU、内存、外设控制器、I/O端口等物理设备)        │
└───────────────────────────────────────────────┘
  1. 过滤驱动(Filter Driver):
    • 位于功能驱动之上,用于扩展或修改设备行为(如 BitLocker 加密过滤、防病毒软件的文件系统过滤)
  2. 功能驱动(Function Driver):
    • 实现设备的核心功能(如硬盘读写、网卡数据收发)是驱动栈的核心。
  3. 总线驱动(Bus Driver):
    • 管理设备与系统的物理连接(如 USB 总线枚举设备、PCIe 分配中断和内存地址)
  4. 硬件抽象层:
    • 将底层硬件差异(如不同CPU架构的中断处理)抽象为统一接口,使内核上层无需关心具体硬件平台。
驱动分层Linux

Linux 采用模块化驱动架构,通过内核模块(Kernel Module)实现分层,核心结构如下:

┌───────────────────────────────────────────────┐
│                 用户空间程序                    │
│  (应用程序、系统工具、库函数如libusb、CUDA等)     │
├───────────────────────────────────────────────┤
│                 系统调用接口                     │
│  (用户空间与内核空间的桥梁,如open()、read()、ioctl()) │
├───────────────────────────────────────────────┤
│                 内核空间驱动                    │
│  ┌───────────────────────────────────────────┐ │
│  │              虚拟文件系统 (VFS)            │ │
│  │  (统一文件、设备、网络的访问接口)            │ │
│  ├───────────────────────────────────────────┤ │
│  │              设备类驱动层                   │ │
│  │  (块设备、字符设备、网络设备等抽象接口)        │ │
│  ├───────────────────────────────────────────┤ │
│  │              总线驱动层                     │ │
│  │  (PCI、USB、I2C、SPI等总线控制器驱动)          │ │
│  ├───────────────────────────────────────────┤ │
│  │              硬件抽象层 (HDA)               │ │
│  │  (部分设备类型特有,如ALSA音频抽象)            │ │
│  └───────────────────────────────────────────┘ │
├───────────────────────────────────────────────┤
│                     硬件                        │
│  (同Windows,物理设备与控制器)                   │
└───────────────────────────────────────────────┘
  1. 虚拟文件系统(VFS):
    • Linux 驱动的核心抽象层,将所有设备(硬盘、键盘、网卡等)统一为 “文件” 接口,应用程序通过open()read()等系统调用访问任何设备。
  2. 设备类驱动(Device Class Driver):
    • 按设备类型划分(如块设备驱动处理硬盘,网络设备驱动处理网卡),提供与 VFS 对接的标准接口(如 file_operations 结构体)
  3. 总线驱动(Bus Driver):
    • 管理设备连接和资源分配(如 USB 主机控制器驱动枚举 USB 设备,分配地址)。
  4. 硬件抽象层(HDA):
    • 部分复杂设备(如音频、图形)的特有抽象层,进一步屏蔽硬件差异(如 ALSA 驱动为不同声卡提供统一 API)
驱动分层优势

驱动程序分层架构,通过清晰的层次划分使得操作系统可以:

  1. 支持海量多样化的硬件设备(每年新增数万种外设);
  2. 快速迭代驱动功能(如更新网卡驱动支持 5G,无需修改上层应用);
  3. 提高系统安全性和稳定性(通过隔离和抽象)。

无论是 Windows、Linux 还是嵌入式系统如 RTOS,分层驱动模型始终是操作系统设计的基石

参考网址

  1. Hook技术:
    • https://www.cnblogs.com/LyShark/p/11692436.html
    • https://cloud.tencent.com/developer/article/2091105
    • https://www.cnblogs.com/from-zero/p/14793057.html
  2. 进程注入:注入进程的24种方式:
    • https://www.cnblogs.com/LittleHann/p/6336950.html
  3. 驱动开发:
    • win驱动:https://blog.csdn.net/m0_46239139/article/details/136086822
    • linux驱动:https://blog.csdn.net/qq21497936/article/details/130534343
  4. 驱动调试:
    • win驱动调试:https://blog.csdn.net/qq_40277608/article/details/109765965
    • linux驱动调试:
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐