【Viusal Studio】关于增量链接机制

概述:Visual Studio 中增量链接(Incremental Linking)的机制与使用问题,使用中遇到的一些问题,简单记录一下

经验丰富的开发、Windows开发可忽略此文

问题描述

注入程序注入的 ShellCode 始终会报错,导致目标程序异常然后终止

场景描述

启用增量链接的情况下,编译如下代码,向目标进程中写入了要执行的函数的 ShellCode,代码逻辑如下所示:

cpp 复制代码
// 省略打开目标进程、申请内存的相关
...
// 要执行的函数
void TargetMessageBox()
{
	reutrn MessageBox(NULL,"你好,我是LCR","提示",MB_OK);
}
...
DWORD dwShellcodeBegin = (DWORD)TargetMessageBox();
...

// 这里直接写入目标函数

WriteProcessMemory(ProcessHandle, TargetMem, (PVOID)dwShellCodeBegin, dwShellCodeLen, NULL);

// 之后通过 NtCreateThreadEx 或者 CreateRemoteThread 创建远程线程

这里先思考下为什么上述代码注入后执行会异常?

...

问题分析

主要问题还是在于这个增量链接:

  • Visual Studio 默认启用的增量链接会生成跳转表
  • 这是为了允许在后续链接中修改函数地址而不必重新编译整个二进制
  • 编译器会为函数创建跳转指令(jmp stub)而非直接引用实际函数地址

主要的问题在于这行代码:

cpp 复制代码
DWORD dwShellcodeBegin = (DWORD)TargetMessageBox();

其获取的地址 dwShellCodeBegin 并非是 TargetMessageBox 的真实地址,而是一个中间地址。这里要了解一个概念 跳转表(jmp stub)

跳转表(jmp stub)

跳转表(Jump Stub)是编译器和链接器生成的一种间接跳转机制,主要用于解决函数调用时的地址重定位问题。它本质上是一小段汇编代码,通常由一个无条件跳转指令(JMP)组成。

  • 典型的x86跳转表包含3部分:
    • 操作码:0xE9(近跳转)
    • 4字节偏移量
    • 实际目标地址
  • 跳转表的优缺点
    • 优点:
      • 提高代码灵活性,支持热更新
      • 减少重新链接的时间
      • 实现地址无关代码
    • 缺点:
      • 增加一次间接跳转,轻微性能影响
      • 增加代码复杂度
      • 可能带来安全风险(如跳转表劫持)

了解增量表之后,大概就知道了,上述代码只是获取了一连串 jmp 的内存,基本如下所示:

基本结构为 e9 + 实际要跳转的偏移量

shellcode 复制代码
0x007DDC45  e9 46 07 06 00  ?F...
0x007DDC4A  e9 51 73 5d 00  ?Qs].
0x007DDC4F  e9 2c c5 56 00  ?,?V.
0x007DDC54  e9 07 4d 18 00  ?.M..
0x007DDC59  e9 92 2b 6a 00  ??+j.
0x007DDC5E  e9 a7 c9 66 00  ???f.
0x007DDC63  e9 78 e4 30 00  ?x?0.
0x007DDC68  e9 a3 db 09 00  ???..
0x007DDC6D  e9 6e a7 0f 00  ?n?..
0x007DDC72  e9 09 ba 06 00  ?.?..
0x007DDC77  e9 c4 c7 10 00  ???..
0x007DDC7C  e9 5f 3c 3a 00  ?_<:.

所以导致注入的 shellcode 在跳转时,跳转了一段无意义的错误地址,进而导致目标进程异常,问题就是这么简单。

解决办法

  1. 关闭增量链接,这是最简单的办法

  2. 获取真实跳转地址,代码也很简单

    cpp 复制代码
    DWORD GetRealAddress(DWORD apparentAddress) {
    	if (*(BYTE*)apparentAddress == 0xE9) { // 检查是否为jmp指令 
    	    return apparentAddress + 5 + *(DWORD*)(apparentAddress + 1);
    	}
    	return apparentAddress; // 如果不是jmp,返回原地址 
    }

代码说明

  1. 跳转指令检测
    *(BYTE*)apparentAddress == 0xE9 这行代码:
    apparentAddress 强制转换为 BYTE* 指针
    解引用获取该地址处的第一个字节
    检查该字节是否为 0xE9 (x86近跳转指令的操作码)
  2. 跳转目标计算
    return apparentAddress + 5 + *(DWORD*)(apparentAddress + 1) 这行代码:
    apparentAddress + 5:跳转指令本身占用5个字节(1字节操作码+4字节偏移量)
    *(DWORD*)(apparentAddress + 1):获取跳转指令后的4字节偏移量
    相加得到最终的跳转目标地址
  3. 跳转指令格式
    x86 近跳转指令(E9)的格式:
    E9 xx xx xx xx
    E9:跳转指令操作码
    后跟4字节的有符号偏移量(小端序存储)
    跳转目标 = 下一条指令地址 + 偏移量
相关推荐
love530love7 小时前
【笔记】ComfyUI “OSError: [WinError 38] 已到文件结尾” 报错解决方案
人工智能·windows·python·aigc·comfyui·winerror 38
Shi_haoliu11 小时前
inno setup6.6.1实例,制作安装包,创建共享文件夹,写入注册表(提供给excel加载项,此文章解释iss文件)
前端·vue.js·windows·excel
nnsix11 小时前
文件系统、分配单元大小 什么意思
windows
Boxsc_midnight12 小时前
【数字人学习之语音合成】Fun-CosyVoice3-0.5B-2512的windows系统中本地部署的方法
windows·学习·cosyvoice3
Zfox_13 小时前
无缝穿越系统边界:节点小宝4.0如何让我的Mac/iOS像访问本地盘一样操控Windows
windows·macos·ios·节点小宝
嵌入式学习和实践13 小时前
Linux/Windows 系统架构查看、安装包选择指南(嵌入式开发场景适配)
linux·windows·系统架构
私人珍藏库13 小时前
[Windows] PDF 专业电子签章工具 v4.8
windows·pdf
一只蚊子014 小时前
C# WinForms配置Halcon
windows·c#·halcon
linksinke14 小时前
在windows系统上搭建Golang多版本管理器(g)的配置环境
开发语言·windows·golang
深兰科技14 小时前
深兰科技入选“2025中国新经济30强(行业之星)”,人工智能产业化能力获认可
人工智能·windows·ci/cd·phpstorm·visual studio code·深兰科技·gyic2025