**郑重声明:**本文所涉安全技术仅限用于合法研究与学习目的,严禁任何形式的非法利用。因不当使用所导致的一切法律与经济责任,本人概不负责。任何形式的转载均须明确标注原文出处,且不得用于商业目的。
🔋 点赞 | 能量注入 ❤️ 关注 | 信号锁定 🔔 收藏 | 数据归档 ⭐️ 评论| 保持连接💬
🌌 立即前往 👉 🚀

▶ 信息收集
▶ 漏洞检测
▶ 初始立足点 ➢ 深入剖析JMP ESP原理 ➢🔥🔥🔥
▶ 权限提升
▶ 横向移动
▶ 报告/分析
▶ 教训/修复
目录
[1.1 复习核心载荷结构](#1.1 复习核心载荷结构)
[1.1.1 再次复习 Python 漏洞利用代码 (42928.py)](#1.1.1 再次复习 Python 漏洞利用代码 (42928.py))
[1.1.1.1 核心载荷分析](#1.1.1.1 核心载荷分析)
[1.1.2.2 POST请求构造](#1.1.2.2 POST请求构造)
[1.2 核心问题剖析](#1.2 核心问题剖析)
[1.2.1 完整的原理剖析](#1.2.1 完整的原理剖析)
[1.2.1.1 构造恶意数据](#1.2.1.1 构造恶意数据)
[1.2.1.2 栈内存状态变化图](#1.2.1.2 栈内存状态变化图)
[1.2.1.3 详细执行流程](#1.2.1.3 详细执行流程)
[1.2.2 如何找到JMP ESP指令地址?](#1.2.2 如何找到JMP ESP指令地址?)
[1.2.3 执行时间线与ESP变化(重要)](#1.2.3 执行时间线与ESP变化(重要))
[1.2.3.1 详细的内存布局图](#1.2.3.1 详细的内存布局图)
[1.2.3.2 执行流程验证](#1.2.3.2 执行流程验证)
[1.2.3.3 形象比喻](#1.2.3.3 形象比喻)
[1.2.3.4 技术细节:寄存器状态变化](#1.2.3.4 技术细节:寄存器状态变化)
[1.2.4 防御视角:如何阻止这种攻击](#1.2.4 防御视角:如何阻止这种攻击)
[1.3 总结核心要点](#1.3 总结核心要点)
[欢迎❤️ 点赞 | 🔔 关注 | ⭐️ 收藏 | 💬 评论](#欢迎❤️ 点赞 | 🔔 关注 | ⭐️ 收藏 | 💬 评论)
1.ESP寄存器在函数返回时的特殊位置
本例以Sync Breeze Enterprise**(这是一个文件管理同步软件)** 为目标,对其漏洞利用分析与修改,并最终利用,利用缓冲区溢出漏洞,类似漏洞编号:CVE-2017-14980。
之前的一系列文章写的是,如果寻找漏洞利用的脚本,并利用缓冲区溢出原理加以利用,按照实际测试环境的情况修改脚本后编译为可执行文件,最终拿到反向shell。主要要非常了解栈结构、出入栈原理,以及shellcode的结构等。但,这里有一个核心问题不知道你们有没有想过:
① "为什么覆盖了返回地址后,当执行漏洞利用文件时,返回地址指向的不是shellcode的地址,而是JMP ESP指令?"
② "为什么返回地址指向JMP ESP指令,就能执行我们的shellcode?"
至少,我脑袋中想到了这个很细节、但有非常核心的问题!那么,我们试试把它弄懂。
1.1 复习核心载荷结构
1.1.1 再次复习 Python 漏洞利用代码 (42928.py)
在此复习42928.py源代码,开始进行分析。
1.1.1.1 核心载荷分析
重点看漏洞利用脚本中的HTTP服务器模块,通过构造特殊的 POST请求 触发缓冲区溢出。以下是核心代码段的解读:
| 代码片段 | 说明 |
|---|---|
offset = "A" * 780 |
创建780个字符"A"作为偏移量填充,用于覆盖缓冲区直至返回地址位置 |
JMP_ESP = "\x83\x0c\x09\x10" |
跳转指令 ,指向内存地址 0x10090c83(JMP ESP),用于重定向执行流。 这里具体的是,在偏移量780处,使用内存地址为0x10090c83的JMP ESP指令覆盖指令指针。 |
shellcode = "\x90"*16 + msf_shellcode |
Shellcode构造 :16个NOP指令(\x90)作为滑道 + 实际负载代码 这个shellcode的作用是执行攻击者想要运行的恶意操作,比如:打开反向Shell连接、执行任意命令等。 |
结构:
python
[缓冲区填充] → [覆盖返回地址,跳转至JMP ESP地址] → [执行NOP滑道] → [运行Shellcode]
1.1.2.2 POST请求构造
漏洞利用通过HTTP POST的请求体携带来发送恶意载荷:
POST请求结构如下:
XML
POST /login HTTP/1.1
Host: <目标IP>
Content-Type: application/x-www-form-urlencoded
Content-Length: <长度>
username=<偏移量><JMP_ESP><NOP滑道><Shellcode>&password=A
1.2 核心问题剖析
"++为什么返回地址指向JMP ESP指令,就能执行我们的Payload?++"
1.2.1 完整的原理剖析
1.2.1.1 构造恶意数据
攻击数据结构:
[低地址] [填充数据] [JMP ESP地址] [NOP滑块] [Payload] [剩余填充] [高地址]
↑ ↑ ↑ ↑
填满缓冲区 覆盖返回地址 安全缓冲 恶意代码
1.2.1.2 栈内存状态变化图
函数返回前栈布局:
高地址
├─────────────────┤
│ Payload │ ← 实际在返回地址之后
├─────────────────┤
│ NOP滑块 │
├─────────────────┤ ← ESP移动到这里(ret执行后)
│ JMP ESP地址 │ ← 返回地址(被覆盖)
├─────────────────┤ ← ESP初始位置(ret执行前)
│ 填充数据 │
│ (缓冲区) │ ← 类似780个A来做填充
低地址
1.2.1.3 详细执行流程
为什么这样设计?------ 解决Payload地址不确定问题
问题根源
-
栈地址可能变化(不同系统、不同运行环境)
-
攻击者无法精确知道Payload在目标栈中的确切地址(解决的核心痛点)
-
直接硬编码Payload地址成功率极低
🧩 JMP ESP的巧妙之处!!!
固定地址问题解决方案:
┌─────────────────────────┬─────────────────────────────┐
│ 直接跳转方案 │ JMP ESP间接方案 │
├─────────────────────────┼─────────────────────────────┤
│ 需要知道Payload的精确地址 │ 只需要知道JMP ESP指令的地址 │
│ 地址随栈变化而变化 │ 地址在系统DLL中固定(无ASLR时)│
│ 成功率低 │ 成功率高 │
└─────────────────────────┴─────────────────────────────┘
1.2.2 如何找到JMP ESP指令地址?
方法1:在系统DLL中搜索
# 示例:在Windows DLL中搜索JMP ESP指令(机器码FF E4)
# 使用调试器或专用工具搜索
JMP_ESP_ADDRESS = 0x75CB52CB # kernel32.dll中的地址
方法2:使用已知通用地址
常见系统JMP ESP地址(无ASLR时):
• Windows XP SP3: 0x7C86467B (kernel32.dll)
• Windows 7 SP1: 0x75C8E2E4 (kernel32.dll)
• Linux (特定版本): 可变,通常需要自定义
方法3:通过调试器获取
1. 在目标环境运行调试器(如OllyDbg、GDB)
2. 加载目标程序
3. 搜索指令 "JMP ESP" 或机器码 "FF E4"
4. 记录找到的地址
1.2.3 执行时间线与ESP变化(重要)

| 布局方式 | 内存结构 | 执行结果 | 为什么 |
|---|---|---|---|
| ❌ 错误布局 | [填充][恶意代码][JMP ESP地址] |
攻击失败 | ESP指向JMP ESP地址之后,但那里没有代码 |
| ✅ 正确布局 | [填充][JMP ESP地址][恶意代码] |
攻击成功 | ESP指向恶意代码开始处 |
1.2.3.1 详细的内存布局图
正确布局(攻击成功)
低地址
├─────────────────┤ ← 缓冲区起始
│ AAAA...AAAA │ ← 填充数据 (填满缓冲区)
├─────────────────┤ ← 原返回地址位置
│ 0x75CB52CB │ ← JMP ESP指令地址 (4字节)
├─────────────────┤ ← ESP执行ret后指向这里!
│ 0x90 0x90... │ ← NOP滑块 (可选,增加容错)
├─────────────────┤
│ shellcode │ ← 恶意代码 (Payload)
│ (恶意指令) │
├─────────────────┤
│ 更多填充... │ ← 如果需要
└─────────────────┘
高地址
1.2.3.2 执行流程验证
1. ret执行前: ESP → 0x75CB52CB (在栈中)
2. ret执行:
- EIP = 0x75CB52CB (JMP ESP指令地址)
- ESP = ESP + 4 → 指向NOP滑块开始处
3. 执行JMP ESP:
- EIP = ESP → 指向NOP滑块开始处
4. 执行NOP滑块 → 滑向shellcode
5. 执行shellcode → 攻击成功!
1.2.3.3 形象比喻
①快递中转站
-
你(攻击者):要寄送一个秘密包裹(Payload)
-
收件人(目标程序):住在经常搬家的公寓(栈地址变化)
-
快递公司(程序执行流):必须把包裹送到正确地址
传统方法的困境
你的计划:直接告诉快递公司"送到A公寓201室"
问题:收件人可能已经搬到B公寓302室
结果:包裹送错地方,任务失败 ❌
JMP ESP方案的巧妙
你的新计划:
1. 告诉快递公司"先送到中央中转站"(JMP ESP地址)
2. 在中转站,有一个智能机器人(JMP ESP指令)
3. 机器人会问:"包裹接下来应该送到哪里?"
4. 快递员(ESP寄存器)回答:"哦,收件人刚刚告诉我,他现在在C公寓105室"
5. 机器人立即把包裹送到C公寓105室(ESP指向的地址)
6. 包裹准确送达,任务成功 ✅
②地铁换乘系统
-
你(CPU):要去朋友家(执行正常代码)
-
攻击者:想把你引导到秘密基地(执行Payload)
-
地铁系统(程序执行流):有固定线路
正常路线(无攻击)
你家 → 乘坐2号线 → 在**人民广场站**换乘 → 乘坐8号线 → 朋友家
攻击者的篡改
攻击者知道:
1.人民广场站(JMP ESP地址) 的位置固定
2.从2号线换乘时,你会自动走到8号线站台(ESP指向的位置)
3.他可以在8号线站台放置引导员(NOP/Payload)
攻击实施
1. 攻击者修改路线图:把"人民广场站"标记为必须换乘站
2. 你到达人民广场站(JMP ESP地址)
3. 车站广播(JMP ESP指令):"请前往您当前所在的站台"
4. 你已经在8号线站台(ESP指向NOP/Payload)
5. 站台上的引导员(NOP)带你到秘密基地(Payload)
为什么这招高明?
-
攻击者无需知道秘密基地的确切地址
-
他只需要知道人民广场站的固定位置
-
地铁系统的换乘逻辑(ret指令) 会自动把你带到正确站台
-
8号线站台总是在人民广场站之后(ESP总指向返回地址后)
为什么这个方案可靠?
-
中转站地址固定:JMP ESP指令在系统DLL中,地址不变
-
总是知道最新地址:ESP寄存器始终指向正确位置
-
总是可用:JMP ESP指令在系统启动时就存在
📊 攻击成功的关键要素表
| 要素 | 要求 | 为什么重要 |
|---|---|---|
| JMP ESP地址 | 必须准确且可执行 | 这是攻击的"跳板",必须可靠 |
| ESP指向 | 必须指向NOP/Payload | 确保跳转后能执行到恶意代码 |
| 缓冲区大小 | 必须精确计算偏移 | 确保JMP ESP地址覆盖正确位置 |
| Payload位置 | 必须在返回地址之后 | 这样ESP才会指向它 |
| 无坏字符 | 避免\x00等特殊字符 | 防止数据被截断 |
1.2.3.4 技术细节:寄存器状态变化
函数返回时刻的寄存器状态:
执行ret前 执行ret后
EIP: 指向ret指令 EIP: JMP ESP指令地址
ESP: 指向返回地址 ESP: 返回地址+4(指向NOP开始)
EBP: 可能被覆盖 EBP: 可能已被破坏
关键计算:
返回地址偏移 = 缓冲区大小 + EBP大小(4字节)
Payload偏移 = 返回地址偏移 + 4(JMP ESP地址大小)
1.2.4 防御视角:如何阻止这种攻击
| 防御技术 | 如何阻止JMP ESP攻击 | 效果 |
|---|---|---|
| ASLR | 随机化系统DLL加载地址 | JMP ESP地址难以预测 |
| DEP/NX | 标记栈为不可执行 | 即使跳转到Payload也无法执行 |
| 栈Canary | 检测返回地址是否被修改 | 在ret执行前就发现攻击 |
| CFG | 控制流保护,限制间接跳转 | 阻止跳转到非常用地址 |
1.3 总结核心要点
-
间接跳转原理 :不直接跳转到Payload,而是通过固定指令中转
-
ESP的关键作用 :自动指向返回地址之后,提供动态跳转目标
-
地址稳定性:JMP ESP指令在系统DLL中,地址相对固定
-
攻击链完整性:每个环节必须精确计算,环环相扣
最终答案:
因为
JMP ESP指令是固定的中转站 ,而ESP寄存器是智能的向导,它总是在正确的时间指向正确的位置。攻击者利用这个系统特性,让不可预测的栈地址问题通过可预测的系统指令解决,从而可靠地执行Payload。
📊 不同类型攻击的布局对比
| 攻击类型 | 内存布局 | 适用场景 |
|---|---|---|
| 直接跳转 | [填充][shellcode地址][shellcode] |
知道shellcode确切地址(概率极低) |
| JMP ESP | [填充][JMP ESP地址][NOP][shellcode] |
栈地址随机,但系统库地址固定(本例) |
| 寄存器跳转 | [填充][JMP EAX地址][shellcode] |
EAX指向shellcode |
欢迎❤️ 点赞 | 🔔 关注 | ⭐️ 收藏 | 💬 评论
每一份支持,都是我持续输出的光。
