1004 lines
31 KiB
HTML
1004 lines
31 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Reverse Engineering IoT Firmware: SPI Flash Extraction | River Rooks</title>
|
|
<style>
|
|
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,700;1,400&family=Inter:ital,wght@0,400;0,500;0,700;1,400&family=JetBrains+Mono:ital,wght@0,400;0,500;1,400&display=swap');
|
|
|
|
:root {
|
|
--bg-canvas: #0d0f12; /* Deep slate/charcoal canvas */
|
|
--bg-surface: #121417; /* Tile & card surface */
|
|
--bg-elevated: #1c1f26; /* Hovered surface */
|
|
--border-color: #2b303b; /* Crisp 1px border */
|
|
--text-primary: #e0e6f0; /* Light off-white prose */
|
|
--text-muted: #8a95a5; /* Monospace metadata */
|
|
--accent-cyan: #00f0ff; /* Active accent */
|
|
--accent-green: #00e676; /* Success / online */
|
|
--accent-amber: #ffb700; /* Warning / highlight */
|
|
--accent-red: #ff5252; /* Danger / caution */
|
|
}
|
|
|
|
* {
|
|
box-sizing: border-box;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
body {
|
|
background-color: var(--bg-canvas);
|
|
color: var(--text-primary);
|
|
font-family: 'Inter', sans-serif;
|
|
line-height: 1.7;
|
|
min-height: 100vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 24px 16px 48px 16px;
|
|
}
|
|
|
|
.tiling-layout {
|
|
width: 100%;
|
|
max-width: 1160px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 16px;
|
|
}
|
|
|
|
/* Top Global Header Bar (Gateway 3 & Tiling Blog Match) */
|
|
.global-bar {
|
|
background-color: var(--bg-surface);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 4px;
|
|
padding: 10px 18px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 0.8rem;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
}
|
|
|
|
.brand-group {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
color: var(--text-primary);
|
|
font-weight: 700;
|
|
}
|
|
|
|
.status-indicator {
|
|
width: 8px;
|
|
height: 8px;
|
|
background-color: var(--accent-green);
|
|
display: inline-block;
|
|
}
|
|
|
|
.bar-nav {
|
|
display: flex;
|
|
gap: 14px;
|
|
align-items: center;
|
|
}
|
|
|
|
.bar-link {
|
|
color: var(--text-muted);
|
|
text-decoration: none;
|
|
transition: none; /* Instant snap */
|
|
}
|
|
|
|
.bar-link:hover {
|
|
color: var(--accent-cyan);
|
|
}
|
|
|
|
/* Article Main Workspace Grid: Left Sidebar + Center Article Content */
|
|
.article-workspace-grid {
|
|
display: grid;
|
|
grid-template-columns: 290px 1fr;
|
|
gap: 16px;
|
|
align-items: start;
|
|
}
|
|
|
|
@media (max-width: 960px) {
|
|
.article-workspace-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
|
|
/* Left Sidebar: Document Outline + Collapsible Related Articles */
|
|
.article-sidebar {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 16px;
|
|
position: sticky;
|
|
top: 16px;
|
|
}
|
|
|
|
.sidebar-tile {
|
|
background-color: var(--bg-surface);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 4px;
|
|
padding: 18px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
|
|
.sidebar-heading {
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 0.82rem;
|
|
font-weight: 700;
|
|
color: var(--text-muted);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
border-bottom: 1px solid var(--border-color);
|
|
padding-bottom: 8px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
/* Document Table of Contents */
|
|
.toc-nav {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 0.78rem;
|
|
}
|
|
|
|
.toc-link {
|
|
color: var(--text-muted);
|
|
text-decoration: none;
|
|
padding: 4px 6px;
|
|
border-radius: 2px;
|
|
transition: none;
|
|
line-height: 1.4;
|
|
display: block;
|
|
}
|
|
|
|
.toc-link:hover {
|
|
background-color: var(--bg-elevated);
|
|
color: var(--accent-cyan);
|
|
}
|
|
|
|
.toc-link.active {
|
|
color: var(--accent-cyan);
|
|
background-color: var(--bg-canvas);
|
|
border-left: 2px solid var(--accent-cyan);
|
|
font-weight: 700;
|
|
}
|
|
|
|
.toc-sub {
|
|
padding-left: 16px;
|
|
color: var(--text-muted);
|
|
font-size: 0.74rem;
|
|
}
|
|
|
|
/* Collapsible Menu Component (<details>) */
|
|
.collapsible-drawer {
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 3px;
|
|
background-color: var(--bg-canvas);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.collapsible-summary {
|
|
background-color: var(--bg-elevated);
|
|
padding: 8px 12px;
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 0.76rem;
|
|
font-weight: 700;
|
|
color: var(--text-primary);
|
|
cursor: pointer;
|
|
list-style: none;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
user-select: none;
|
|
}
|
|
|
|
.collapsible-summary::-webkit-details-marker {
|
|
display: none;
|
|
}
|
|
|
|
.collapsible-summary::after {
|
|
content: "[+]";
|
|
color: var(--accent-cyan);
|
|
font-weight: 700;
|
|
}
|
|
|
|
.collapsible-drawer[open] .collapsible-summary::after {
|
|
content: "[-]";
|
|
}
|
|
|
|
.drawer-content {
|
|
padding: 10px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
}
|
|
|
|
.related-article-card {
|
|
background-color: var(--bg-surface);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 2px;
|
|
padding: 8px 10px;
|
|
text-decoration: none;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 3px;
|
|
transition: none;
|
|
}
|
|
|
|
.related-article-card:hover {
|
|
border-color: var(--accent-cyan);
|
|
background-color: var(--bg-elevated);
|
|
}
|
|
|
|
.related-date {
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 0.7rem;
|
|
color: var(--accent-cyan);
|
|
}
|
|
|
|
.related-title {
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 0.8rem;
|
|
font-weight: 700;
|
|
color: var(--text-primary);
|
|
line-height: 1.35;
|
|
}
|
|
|
|
.related-article-card:hover .related-title {
|
|
color: var(--accent-cyan);
|
|
}
|
|
|
|
.related-tags {
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 0.68rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
/* Article Content Container Tile */
|
|
.article-tile {
|
|
background-color: var(--bg-surface);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 4px;
|
|
padding: 36px 40px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 24px;
|
|
}
|
|
|
|
@media (max-width: 600px) {
|
|
.article-tile {
|
|
padding: 24px 18px;
|
|
}
|
|
}
|
|
|
|
/* Article Header Block */
|
|
.article-header-block {
|
|
border-bottom: 1px solid var(--border-color);
|
|
padding-bottom: 24px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
|
|
.article-breadcrumbs {
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 0.78rem;
|
|
color: var(--text-muted);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.article-main-title {
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 2.3rem;
|
|
font-weight: 700;
|
|
color: var(--text-primary);
|
|
letter-spacing: -0.02em;
|
|
line-height: 1.25;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.article-main-title {
|
|
font-size: 1.8rem;
|
|
}
|
|
}
|
|
|
|
.article-meta-row {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 14px;
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 0.8rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.meta-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
|
|
.tag-chip {
|
|
background-color: var(--bg-canvas);
|
|
border: 1px solid var(--border-color);
|
|
padding: 2px 6px;
|
|
border-radius: 2px;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
/* ==========================================================================
|
|
Markdown Render Showcase Styles
|
|
========================================================================== */
|
|
|
|
.markdown-prose {
|
|
max-width: 68ch; /* Capped reading density */
|
|
font-size: 1.02rem;
|
|
color: var(--text-primary);
|
|
line-height: 1.7;
|
|
}
|
|
|
|
.markdown-prose p {
|
|
margin-bottom: 18px;
|
|
}
|
|
|
|
/* Headings */
|
|
.markdown-prose h2 {
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 1.45rem;
|
|
font-weight: 700;
|
|
color: var(--text-primary);
|
|
margin: 36px 0 16px 0;
|
|
padding-bottom: 8px;
|
|
border-bottom: 1px solid var(--border-color);
|
|
letter-spacing: -0.01em;
|
|
}
|
|
|
|
.markdown-prose h3 {
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 1.15rem;
|
|
font-weight: 700;
|
|
color: var(--text-primary);
|
|
margin: 28px 0 12px 0;
|
|
}
|
|
|
|
/* Links */
|
|
.markdown-prose a {
|
|
color: var(--accent-cyan);
|
|
text-decoration: underline;
|
|
text-underline-offset: 3px;
|
|
}
|
|
|
|
.markdown-prose a:hover {
|
|
background-color: var(--accent-cyan);
|
|
color: var(--bg-canvas);
|
|
text-decoration: none;
|
|
}
|
|
|
|
/* Inline Code */
|
|
.markdown-prose code:not(pre code) {
|
|
font-family: 'JetBrains Mono', monospace;
|
|
font-size: 0.88rem;
|
|
background-color: var(--bg-canvas);
|
|
border: 1px solid var(--border-color);
|
|
padding: 2px 6px;
|
|
border-radius: 3px;
|
|
color: var(--accent-cyan);
|
|
}
|
|
|
|
/* Code Blocks with Terminal Titlebars */
|
|
.code-block-container {
|
|
margin: 22px 0;
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 4px;
|
|
background-color: var(--bg-canvas);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.code-block-header {
|
|
background-color: var(--bg-surface);
|
|
border-bottom: 1px solid var(--border-color);
|
|
padding: 6px 14px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 0.74rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.code-title {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
color: var(--text-primary);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.copy-code-btn {
|
|
background: transparent;
|
|
border: 1px solid var(--border-color);
|
|
color: var(--text-muted);
|
|
padding: 2px 8px;
|
|
font-family: inherit;
|
|
font-size: 0.72rem;
|
|
cursor: pointer;
|
|
border-radius: 2px;
|
|
transition: none;
|
|
}
|
|
|
|
.copy-code-btn:hover {
|
|
border-color: var(--accent-cyan);
|
|
color: var(--accent-cyan);
|
|
}
|
|
|
|
.code-block-container pre {
|
|
padding: 14px 16px;
|
|
overflow-x: auto;
|
|
font-family: 'JetBrains Mono', monospace;
|
|
font-size: 0.82rem;
|
|
line-height: 1.5;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
/* GitHub-style Markdown Alerts / Callouts */
|
|
.markdown-callout {
|
|
margin: 20px 0;
|
|
padding: 14px 18px;
|
|
border-radius: 3px;
|
|
border: 1px solid var(--border-color);
|
|
background-color: var(--bg-canvas);
|
|
font-size: 0.94rem;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.callout-note {
|
|
border-left: 4px solid var(--accent-cyan);
|
|
}
|
|
|
|
.callout-warning {
|
|
border-left: 4px solid var(--accent-amber);
|
|
}
|
|
|
|
.callout-title {
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-weight: 700;
|
|
font-size: 0.82rem;
|
|
margin-bottom: 6px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
|
|
.callout-note .callout-title {
|
|
color: var(--accent-cyan);
|
|
}
|
|
|
|
.callout-warning .callout-title {
|
|
color: var(--accent-amber);
|
|
}
|
|
|
|
/* Markdown Tables */
|
|
.table-container {
|
|
width: 100%;
|
|
overflow-x: auto;
|
|
margin: 22px 0;
|
|
}
|
|
|
|
.markdown-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
font-size: 0.86rem;
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
text-align: left;
|
|
}
|
|
|
|
.markdown-table th {
|
|
background-color: var(--bg-canvas);
|
|
border: 1px solid var(--border-color);
|
|
padding: 8px 12px;
|
|
color: var(--text-muted);
|
|
font-weight: 700;
|
|
}
|
|
|
|
.markdown-table td {
|
|
border: 1px solid var(--border-color);
|
|
padding: 8px 12px;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.markdown-table tr:hover {
|
|
background-color: var(--bg-elevated);
|
|
}
|
|
|
|
/* Lists & Task Lists */
|
|
.markdown-prose ul, .markdown-prose ol {
|
|
margin: 16px 0 18px 24px;
|
|
}
|
|
|
|
.markdown-prose li {
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.task-list {
|
|
list-style: none;
|
|
margin-left: 0 !important;
|
|
}
|
|
|
|
.task-list-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
margin-bottom: 8px;
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 0.88rem;
|
|
}
|
|
|
|
.task-box {
|
|
width: 14px;
|
|
height: 14px;
|
|
border: 1px solid var(--accent-cyan);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 0.65rem;
|
|
color: var(--accent-cyan);
|
|
background-color: var(--bg-canvas);
|
|
}
|
|
|
|
/* Keyboard Shortcuts <kbd> */
|
|
kbd {
|
|
background-color: var(--bg-canvas);
|
|
border: 1px solid var(--border-color);
|
|
border-bottom: 2px solid var(--text-muted);
|
|
padding: 1px 5px;
|
|
border-radius: 3px;
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 0.78rem;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
/* Global Footer Tile (Gateway 3 Exact Match) */
|
|
.footer-tile {
|
|
background-color: var(--bg-surface);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 4px;
|
|
padding: 12px 20px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
font-family: 'IBM Plex Mono', monospace;
|
|
font-size: 0.78rem;
|
|
color: var(--text-muted);
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
}
|
|
|
|
.footer-links-cluster {
|
|
display: flex;
|
|
gap: 12px;
|
|
align-items: center;
|
|
}
|
|
|
|
.footer-btn {
|
|
color: var(--text-primary);
|
|
text-decoration: none;
|
|
padding: 2px 4px;
|
|
transition: none;
|
|
}
|
|
|
|
.footer-btn:hover {
|
|
background-color: var(--accent-cyan);
|
|
color: var(--bg-canvas);
|
|
}
|
|
|
|
.pipe {
|
|
color: var(--border-color);
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<main class="tiling-layout">
|
|
|
|
<!-- Top Global Header Bar (Matching Gateway 3 Exactly) -->
|
|
<header class="global-bar">
|
|
<div class="brand-group">
|
|
<span class="status-indicator"></span>
|
|
<span>RIVER ROOKS // ARTICLE VIEWER</span>
|
|
</div>
|
|
<nav class="bar-nav" aria-label="Navigation">
|
|
<a href="service-gateway-tiling.html" class="bar-link">Gateway 3 ↗</a>
|
|
<a href="blog-tiling-lead.html" class="bar-link">← Back to Blog Feed</a>
|
|
<a href="https://git.rooks.dev" target="_blank" rel="noopener" class="bar-link">Git Forge</a>
|
|
<a href="feed.xml" class="bar-link">RSS</a>
|
|
<a href="index.html" class="bar-link">Catalog ↗</a>
|
|
</nav>
|
|
</header>
|
|
|
|
<!-- Main Workspace Grid: Sidebar Navigation + Article Content -->
|
|
<div class="article-workspace-grid">
|
|
|
|
<!-- Left Sidebar: Document Outline + Collapsible Related Articles -->
|
|
<aside class="article-sidebar" aria-label="Article Table of Contents & Related Dispatches">
|
|
|
|
<!-- Document Outline (TOC) Tile -->
|
|
<div class="sidebar-tile">
|
|
<div class="sidebar-heading">
|
|
<span>DOCUMENT OUTLINE</span>
|
|
<span style="color: var(--accent-cyan);">H2 / H3</span>
|
|
</div>
|
|
|
|
<nav class="toc-nav" aria-label="Table of Contents">
|
|
<a href="#overview" class="toc-link active">> 1.0 Executive Summary</a>
|
|
<a href="#hardware-pinout" class="toc-link">> 2.0 Hardware Pinout & Bus Pirate</a>
|
|
<a href="#pinout-matrix" class="toc-link toc-sub">2.1 SOP8 Pinout Matrix</a>
|
|
<a href="#spi-extraction" class="toc-link">> 3.0 SPI Extraction via flashrom</a>
|
|
<a href="#binary-verification" class="toc-link">> 4.0 Binary Verification & Hashes</a>
|
|
<a href="#post-mortem-checklist" class="toc-link">> 5.0 Audit Checklist</a>
|
|
</nav>
|
|
</div>
|
|
|
|
<!-- Collapsible Related Articles Drawer Tile -->
|
|
<div class="sidebar-tile">
|
|
<div class="sidebar-heading">
|
|
<span>RELEVANT DISPATCHES</span>
|
|
<span>DRAWER</span>
|
|
</div>
|
|
|
|
<!-- Collapsible Menu -->
|
|
<details class="collapsible-drawer" open>
|
|
<summary class="collapsible-summary">
|
|
<span>Related Series (4)</span>
|
|
</summary>
|
|
<div class="drawer-content">
|
|
|
|
<a href="article-tiling-traefik.html" class="related-article-card">
|
|
<span class="related-date">2026-07-15 • 12 min</span>
|
|
<span class="related-title">High-Availability Traefik Router with Docker Compose</span>
|
|
<span class="related-tags">[#homelab] [#networking]</span>
|
|
</a>
|
|
|
|
<a href="article-tiling-heap.html" class="related-article-card">
|
|
<span class="related-date">2026-06-28 • 15 min</span>
|
|
<span class="related-title">CTF Write-up: Heap Exploitation in glibc 2.35</span>
|
|
<span class="related-tags">[#ctf] [#security]</span>
|
|
</a>
|
|
|
|
<a href="blog-tiling-lead.html" class="related-article-card">
|
|
<span class="related-date">2026-07-10 • 6 min</span>
|
|
<span class="related-title">POSIX sh vs. Bash: Portability in Modern Scripts</span>
|
|
<span class="related-tags">[#posix-sh] [#unix]</span>
|
|
</a>
|
|
|
|
<a href="blog-tiling-lead.html" class="related-article-card">
|
|
<span class="related-date">2026-06-14 • 10 min</span>
|
|
<span class="related-title">Zero-Trust Network Isolation with WireGuard</span>
|
|
<span class="related-tags">[#wireguard] [#networking]</span>
|
|
</a>
|
|
|
|
</div>
|
|
</details>
|
|
</div>
|
|
|
|
<!-- Terminal Quick Action Box -->
|
|
<div class="sidebar-tile" style="font-family: 'IBM Plex Mono', monospace; font-size: 0.76rem; color: var(--text-muted);">
|
|
<div style="display: flex; justify-content: space-between; margin-bottom: 6px;">
|
|
<span>READING TIME:</span>
|
|
<span style="color: var(--text-primary); font-weight: 700;">8 MINUTES</span>
|
|
</div>
|
|
<div style="display: flex; justify-content: space-between; margin-bottom: 6px;">
|
|
<span>TARGET SILICON:</span>
|
|
<span style="color: var(--accent-cyan); font-weight: 700;">WINBOND 25Q64</span>
|
|
</div>
|
|
<div style="display: flex; justify-content: space-between;">
|
|
<span>FILE HASH:</span>
|
|
<span style="color: var(--accent-green);">SHA256:7uK+N0...</span>
|
|
</div>
|
|
</div>
|
|
|
|
</aside>
|
|
|
|
<!-- Main Article Reading Container Tile -->
|
|
<article class="article-tile">
|
|
|
|
<!-- Article Header Block -->
|
|
<header class="article-header-block">
|
|
<div class="article-breadcrumbs">
|
|
<span>~/notes/</span>
|
|
<span>></span>
|
|
<span style="color: var(--accent-cyan);">iot-firmware-spi-extraction.md</span>
|
|
</div>
|
|
|
|
<h1 class="article-main-title">Reverse Engineering IoT Firmware: SPI Flash Extraction</h1>
|
|
|
|
<div class="article-meta-row">
|
|
<div class="meta-item">
|
|
<span style="color: var(--accent-cyan);">2026-07-20</span>
|
|
</div>
|
|
<span>•</span>
|
|
<div class="meta-item">
|
|
<span>8 MIN READ</span>
|
|
</div>
|
|
<span>•</span>
|
|
<div class="meta-item">
|
|
<span>AUTHOR: RIVER ROOKS</span>
|
|
</div>
|
|
<span>•</span>
|
|
<div class="meta-item">
|
|
<span class="tag-chip">[#firmware]</span>
|
|
<span class="tag-chip">[#hardware]</span>
|
|
<span class="tag-chip">[#security]</span>
|
|
<span class="tag-chip">[#bus-pirate]</span>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Markdown Content Body -->
|
|
<div class="markdown-prose">
|
|
|
|
<!-- Section 1.0 -->
|
|
<h2 id="overview">1.0 Executive Summary</h2>
|
|
<p>
|
|
In security audits of embedded systems, retrieving the firmware image is the critical foundational step. When manufacturers encrypt over-the-air (OTA) updates and blow electronic fuses to permanently disable silicon debug ports (such as <code>UART</code> serial consoles and <code>JTAG/SWD</code> test pads), physical memory extraction directly off the circuit board becomes mandatory.
|
|
</p>
|
|
<p>
|
|
This writeup details the complete process of extracting a binary image from an 8-pin SOP SPI flash chip (Winbond W25Q64JV, 8MB) using a <strong>Dangerous Prototypes Bus Pirate v4</strong> hardware bridge and the open-source <code>flashrom</code> utility.
|
|
</p>
|
|
|
|
<!-- Alert Callout Showcase -->
|
|
<div class="markdown-callout callout-note">
|
|
<div class="callout-title">[!NOTE] Target Board Isolation</div>
|
|
<p>
|
|
Always de-solder the SPI flash chip or clamp using an SOP8 test clip with the host microcontroller's reset line (<code>/RESET</code>) driven low to prevent the main SoC from contending on the SPI bus.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Section 2.0 -->
|
|
<h2 id="hardware-pinout">2.0 Hardware Pinout & Bus Pirate Setup</h2>
|
|
<p>
|
|
The target is an 8-pin Small Outline Package (SOP-8). Pin 1 is easily identified by the circular depression in the epoxy mold. Connect the Bus Pirate flying leads directly according to the standard SPI protocol mapping:
|
|
</p>
|
|
|
|
<h3 id="pinout-matrix">2.1 SOP8 Pinout Matrix</h3>
|
|
|
|
<!-- Table Showcase -->
|
|
<div class="table-container">
|
|
<table class="markdown-table">
|
|
<thead>
|
|
<tr>
|
|
<th>SOP8 Pin #</th>
|
|
<th>Signal Name</th>
|
|
<th>Bus Pirate Lead</th>
|
|
<th>Electrical Purpose</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>1</td>
|
|
<td><code>/CS</code></td>
|
|
<td>CS (White)</td>
|
|
<td>Chip Select (Active Low)</td>
|
|
</tr>
|
|
<tr>
|
|
<td>2</td>
|
|
<td><code>DO (MISO)</code></td>
|
|
<td>MISO (Black)</td>
|
|
<td>Serial Data Output from Flash</td>
|
|
</tr>
|
|
<tr>
|
|
<td>3</td>
|
|
<td><code>/WP</code></td>
|
|
<td>+3.3V (VPU)</td>
|
|
<td>Write Protect (Tied High to Disable)</td>
|
|
</tr>
|
|
<tr>
|
|
<td>4</td>
|
|
<td><code>GND</code></td>
|
|
<td>GND (Brown)</td>
|
|
<td>System Ground Reference</td>
|
|
</tr>
|
|
<tr>
|
|
<td>5</td>
|
|
<td><code>DI (MOSI)</code></td>
|
|
<td>MOSI (Grey)</td>
|
|
<td>Serial Data Input to Flash</td>
|
|
</tr>
|
|
<tr>
|
|
<td>6</td>
|
|
<td><code>CLK</code></td>
|
|
<td>CLK (Purple)</td>
|
|
<td>Serial Clock Driven by Master</td>
|
|
</tr>
|
|
<tr>
|
|
<td>7</td>
|
|
<td><code>/HOLD</code></td>
|
|
<td>+3.3V (VPU)</td>
|
|
<td>Hold / Suspend Input (Tied High)</td>
|
|
</tr>
|
|
<tr>
|
|
<td>8</td>
|
|
<td><code>VCC</code></td>
|
|
<td>+3.3V (Red)</td>
|
|
<td>Supply Voltage (Nominal 3.3V DC)</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="markdown-callout callout-warning">
|
|
<div class="callout-title">[!WARNING] Electrical Voltage Rating</div>
|
|
<p>
|
|
Verify whether your target chip is a <strong>3.3V</strong> part (e.g., <code>W25Q64FV</code>) or a <strong>1.8V</strong> part (e.g., <code>W25Q64FW</code>). Supplying 3.3V logic to a 1.8V chip will permanently destroy the silicon junction gates.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Section 3.0 -->
|
|
<h2 id="spi-extraction">3.0 SPI Extraction via flashrom</h2>
|
|
<p>
|
|
Plug the Bus Pirate into your analysis workstation via USB. It registers as <code>/dev/ttyUSB0</code>. Query the chip ID register using the <code>0x9F</code> JEDEC Read ID command:
|
|
</p>
|
|
|
|
<!-- Code Block Showcase -->
|
|
<div class="code-block-container">
|
|
<div class="code-block-header">
|
|
<div class="code-title">
|
|
<span>extract_firmware.sh</span>
|
|
<span>POSIX sh</span>
|
|
</div>
|
|
<button class="copy-code-btn" onclick="copyCode(this)">Copy Code</button>
|
|
</div>
|
|
<pre><code>#!/bin/sh
|
|
# Query the SPI flash details using flashrom via Bus Pirate
|
|
flashrom -p buspirate_spi:dev=/dev/ttyUSB0,spispeed=1M -r firmware_dump1.bin
|
|
|
|
# Repeat extraction to create a secondary dump for comparison
|
|
flashrom -p buspirate_spi:dev=/dev/ttyUSB0,spispeed=1M -r firmware_dump2.bin
|
|
|
|
# Verify the integrity of the binary dumps by diffing
|
|
cmp firmware_dump1.bin firmware_dump2.bin
|
|
if [ $? -eq 0 ]; then
|
|
echo "[+] SPI Dumps match identically! Proceeding to hashing..."
|
|
md5sum firmware_dump1.bin
|
|
sha256sum firmware_dump1.bin
|
|
else
|
|
echo "[-] Bit flip or voltage sag detected! Re-run extraction." >&2
|
|
exit 1
|
|
fi</code></pre>
|
|
</div>
|
|
|
|
<p>
|
|
Press <kbd>Ctrl</kbd> + <kbd>C</kbd> if the bus fails to synchronize. Once complete, inspect the initial bytes with <code>xxd</code> or <code>hexdump</code>:
|
|
</p>
|
|
|
|
<div class="code-block-container">
|
|
<div class="code-block-header">
|
|
<div class="code-title">
|
|
<span>stdout: hexdump inspection</span>
|
|
<span>Hex View</span>
|
|
</div>
|
|
<button class="copy-code-btn" onclick="copyCode(this)">Copy Output</button>
|
|
</div>
|
|
<pre><code>0000000: 2705 1956 7062 8251 60a4 2130 0041 5262 '..Vpb.Q`.!0.ARb
|
|
0000010: 6873 7173 0000 0004 0000 0000 0000 1000 hsqs............
|
|
0000020: 0002 0001 0000 0000 636f 6e66 6967 2e64 ........config.d</code></pre>
|
|
</div>
|
|
|
|
<p>
|
|
Notice the magic bytes <code>6873 7173</code> (ASCII representation: <strong><code>hsqs</code></strong>) at offset <code>0x000010</code>. This confirms the presence of a SquashFS compressed root filesystem!
|
|
</p>
|
|
|
|
<!-- Section 4.0 -->
|
|
<h2 id="binary-verification">4.0 Binary Verification & Hashes</h2>
|
|
<p>
|
|
Before proceeding with automated deconstruction using <code>binwalk -Me</code>, record cryptographic hashes in your lab notebook:
|
|
</p>
|
|
|
|
<ul>
|
|
<li><strong>Dump Size:</strong> <code>8,388,608 bytes</code> (8192 KiB)</li>
|
|
<li><strong>MD5:</strong> <code>d41d8cd98f00b204e9800998ecf8427e</code></li>
|
|
<li><strong>SHA-256:</strong> <code>a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e</code></li>
|
|
</ul>
|
|
|
|
<!-- Section 5.0 -->
|
|
<h2 id="post-mortem-checklist">5.0 Audit Checklist</h2>
|
|
<p>
|
|
Ensure all forensics hygiene steps have been recorded:
|
|
</p>
|
|
|
|
<ul class="task-list">
|
|
<li class="task-list-item">
|
|
<span class="task-box">✓</span>
|
|
<span>Visual optical inspection and pinout diagram documented</span>
|
|
</li>
|
|
<li class="task-list-item">
|
|
<span class="task-box">✓</span>
|
|
<span>Clean dual flashrom dumps matching via bitwise <code>cmp</code></span>
|
|
</li>
|
|
<li class="task-list-item">
|
|
<span class="task-box">✓</span>
|
|
<span>SquashFS magic header identified at offset <code>0x10</code></span>
|
|
</li>
|
|
<li class="task-list-item">
|
|
<span class="task-box" style="color: var(--text-muted);">•</span>
|
|
<span>Filesystem extraction and hardcoded credentials analysis (Part 2)</span>
|
|
</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
</article>
|
|
|
|
</div>
|
|
|
|
<!-- Global Footer Tile (Gateway 3 Exact Match) -->
|
|
<footer class="footer-tile">
|
|
<div>River Rooks • University of Alabama College of Engineering</div>
|
|
<nav class="footer-links-cluster" aria-label="Global Footer Links">
|
|
<a href="service-gateway-tiling.html" class="footer-btn">Gateway Lander</a>
|
|
<span class="pipe">|</span>
|
|
<a href="feed.xml" class="footer-btn">RSS</a>
|
|
<span class="pipe">|</span>
|
|
<a href="https://git.rooks.dev" target="_blank" rel="noopener" class="footer-btn">Forgejo</a>
|
|
<span class="pipe">|</span>
|
|
<a href="mailto:river@rooks.dev" class="footer-btn">river@rooks.dev</a>
|
|
<span class="pipe">|</span>
|
|
<a href="#" title="Fingerprint: 0x4A8E92F1C0B3D4E7" class="footer-btn">PGP</a>
|
|
<span class="pipe">|</span>
|
|
<a href="index.html" class="footer-btn">Catalog ↗</a>
|
|
</nav>
|
|
</footer>
|
|
|
|
</main>
|
|
|
|
<script>
|
|
function copyCode(btn) {
|
|
const code = btn.closest('.code-block-container').querySelector('pre').innerText;
|
|
navigator.clipboard.writeText(code).then(() => {
|
|
const originalText = btn.innerText;
|
|
btn.innerText = 'Copied!';
|
|
btn.style.color = 'var(--accent-green)';
|
|
btn.style.borderColor = 'var(--accent-green)';
|
|
setTimeout(() => {
|
|
btn.innerText = originalText;
|
|
btn.style.color = '';
|
|
btn.style.borderColor = '';
|
|
}, 1800);
|
|
});
|
|
}
|
|
|
|
// Dynamic TOC highlighting on scroll
|
|
window.addEventListener('DOMContentLoaded', () => {
|
|
const links = document.querySelectorAll('.toc-link');
|
|
const sections = [];
|
|
|
|
links.forEach(link => {
|
|
const targetId = link.getAttribute('href').substring(1);
|
|
const section = document.getElementById(targetId);
|
|
if (section) sections.push({ link, section });
|
|
});
|
|
|
|
window.addEventListener('scroll', () => {
|
|
const scrollPos = window.scrollY + 100;
|
|
for (let i = sections.length - 1; i >= 0; i--) {
|
|
if (sections[i].section.offsetTop <= scrollPos) {
|
|
links.forEach(l => l.classList.remove('active'));
|
|
sections[i].link.classList.add('active');
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|