mirror of
https://github.com/czcdr/dsh-plugin-rea.git
synced 2026-11-07 10:25:59 -10:00
The market page renders `assets/screenshot-1.png` straight from GitHub HEAD, so whatever is committed here is what shoppers see. It still said "DSH desktop runtime 0.2.0-rc.2" and "2026-10-26" — two months and several host releases behind what this plugin was actually measured against. - the host version became a variable (`$hostMeasured`); it has no manifest field to read because it is not a property of this package, so it carries a comment saying it must move with the measurement - `$measuredOn` is 2026-11-07, the date of the last real `--reuse` run (smoke imported java.exe in 31 s), and the stale hardcoded defaults (6.7.0 / 24.21.0) were brought up to the current pins so a failed manifest read no longer paints old numbers - both PNGs regenerated; screenshot-2 carries no version text and is unchanged in content The npm tarball for 2.0.16 still holds the previous images — that is harmless, because the market reads these files from HEAD, not from the package.
200 lines
11 KiB
PowerShell
200 lines
11 KiB
PowerShell
<#
|
||
生成市场展示图(assets/screenshot-*.png)。
|
||
可复现:改完文案后重跑本脚本即可,无需设计工具。
|
||
|
||
运行方式:
|
||
Windows PowerShell 5.1 : powershell -NoProfile -File .\make-screenshots.ps1
|
||
PowerShell 7 (pwsh) : pwsh -NoProfile -File ./make-screenshots.ps1
|
||
两者都可用;脚本内部用 OutDir 参数,默认写到本文件所在目录。
|
||
|
||
注意:Windows PowerShell 5.1 按 ANSI 读取 .ps1,本文件必须保存为 UTF-8 with BOM
|
||
(用 pwsh 7 编辑则无此限制,但保持 BOM 对两者都安全)。
|
||
#>
|
||
[CmdletBinding()]
|
||
param([string]$OutDir)
|
||
|
||
# The documented invocation (`powershell -File .\make-screenshots.ps1`) died right here:
|
||
# "Cannot bind argument to parameter 'Path' because it is an empty string" — $PSScriptRoot
|
||
# is not reliably populated while parameter defaults are bound, so this script had never
|
||
# actually regenerated a screenshot. Resolve the default in the body instead.
|
||
if (-not $OutDir -or $OutDir.Trim() -eq '') { $OutDir = $PSScriptRoot }
|
||
|
||
# 版本号从 manifest 读,不再手写成固定值。2.0.10 就踩过:pin 升到 6.7.0 之后,
|
||
# 这个脚本还写着 6.5.0 —— 现有 PNG 没问题(它们是 6.5.0 时生成的),但下一次
|
||
# 重绘会把旧版本号带进市场页,而且没有人会注意到,因为图片是二进制。
|
||
# 工具数(229)不是 manifest 字段,是人工核对过的常量:升 pin 时一起核一遍。
|
||
$reaPin = '6.8.0'
|
||
$reaTools = 229
|
||
$ghidraPin = '12.1.4'
|
||
$nodePin = '24.22.1'
|
||
# 截图是一次「实测」,这个日期必须是真的测量日;重绘时改成当天。
|
||
# 2026-11-07:2.0.16 发布前的最后一次真机 --reuse(smoke 31 s 导入 java.exe)。
|
||
$measuredOn = '2026-11-07'
|
||
# 宿主版本没有 manifest 字段可读 —— 它不是本包的属性。写死但必须跟着实测更新:
|
||
# 2.0.16 的实测宿主是 desktop runtime 0.2.2-rc.1,之前这里写的是 0.2.0-rc.2,
|
||
# 于是市场页在展示一个两个月前的宿主。
|
||
$hostMeasured = '0.2.2-rc.1'
|
||
$manifestFile = Join-Path (Split-Path -Parent $PSScriptRoot) 'manifest\bundle.manifest.json'
|
||
if (Test-Path $manifestFile) {
|
||
try {
|
||
$pinDoc = Get-Content $manifestFile -Raw -Encoding UTF8 | ConvertFrom-Json
|
||
if ($pinDoc.pins.reaAgents) { $reaPin = [string]$pinDoc.pins.reaAgents }
|
||
if ($pinDoc.pins.ghidra) { $ghidraPin = [string]$pinDoc.pins.ghidra }
|
||
if ($pinDoc.pins.node) { $nodePin = [string]$pinDoc.pins.node }
|
||
} catch {
|
||
Write-Warning "manifest 里的钉子读不到($($_.Exception.Message));用脚本内的默认值。"
|
||
}
|
||
}
|
||
# 实测版本未必等于钉:本机若开过 Ghidra 闸门覆盖(scripts/ghidra-gate-override.mjs),
|
||
# 跑的是 REA 未验证的那一版。图上写实测的那个,并标出钉值 —— 否则图会谎报「跑过什么」。
|
||
$ghidraMeasured = $ghidraPin
|
||
$overrideRecord = $null
|
||
if ($env:DSH_HOME) {
|
||
$overrideFile = Join-Path $env:DSH_HOME 'plugins\rea-bundle\ghidra-gate-override.json'
|
||
if (Test-Path $overrideFile) {
|
||
try {
|
||
$overrideRecord = Get-Content $overrideFile -Raw -Encoding UTF8 | ConvertFrom-Json
|
||
if ($overrideRecord.requestedVersion) { $ghidraMeasured = "$($overrideRecord.requestedVersion)(闸门覆盖,钉 $ghidraPin)" }
|
||
} catch { $overrideRecord = $null }
|
||
}
|
||
}
|
||
|
||
Add-Type -AssemblyName System.Drawing
|
||
|
||
$W = 1280; $H = 720
|
||
$bg = [System.Drawing.Color]::FromArgb(255, 248, 250, 252)
|
||
$ink = [System.Drawing.Color]::FromArgb(255, 15, 23, 42)
|
||
$muted = [System.Drawing.Color]::FromArgb(255, 100, 116, 139)
|
||
$accent = [System.Drawing.Color]::FromArgb(255, 37, 99, 235)
|
||
$accent2 = [System.Drawing.Color]::FromArgb(255, 22, 163, 74)
|
||
$warn = [System.Drawing.Color]::FromArgb(255, 220, 38, 38)
|
||
$line = [System.Drawing.Color]::FromArgb(255, 203, 213, 225)
|
||
|
||
$fTitle = New-Object System.Drawing.Font('Microsoft YaHei UI', 30, [System.Drawing.FontStyle]::Bold)
|
||
$fSub = New-Object System.Drawing.Font('Microsoft YaHei UI', 15)
|
||
$fBox = New-Object System.Drawing.Font('Microsoft YaHei UI', 15, [System.Drawing.FontStyle]::Bold)
|
||
$fBody = New-Object System.Drawing.Font('Microsoft YaHei UI', 13)
|
||
$fSmall = New-Object System.Drawing.Font('Microsoft YaHei UI', 11)
|
||
$fChip = New-Object System.Drawing.Font('Microsoft YaHei UI', 13)
|
||
|
||
function New-Canvas {
|
||
$bmp = New-Object System.Drawing.Bitmap $W, $H
|
||
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
||
$g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
|
||
$g.TextRenderingHint = [System.Drawing.Text.TextRenderingHint]::ClearTypeGridFit
|
||
$g.Clear($bg)
|
||
return @($bmp, $g)
|
||
}
|
||
function Draw-Text($g, $text, $font, $color, $x, $y, $w = 0) {
|
||
$br = New-Object System.Drawing.SolidBrush $color
|
||
if ($w -gt 0) { $r = New-Object System.Drawing.RectangleF $x, $y, $w, 400; $g.DrawString($text, $font, $br, $r) }
|
||
else { $g.DrawString($text, $font, $br, $x, $y) }
|
||
$br.Dispose()
|
||
}
|
||
function Draw-Box($g, $x, $y, $w, $h, $fill, $border, $radius = 12) {
|
||
$path = New-Object System.Drawing.Drawing2D.GraphicsPath
|
||
$d = $radius * 2
|
||
$path.AddArc($x, $y, $d, $d, 180, 90); $path.AddArc($x + $w - $d, $y, $d, $d, 270, 90)
|
||
$path.AddArc($x + $w - $d, $y + $h - $d, $d, $d, 0, 90); $path.AddArc($x, $y + $h - $d, $d, $d, 90, 90)
|
||
$path.CloseFigure()
|
||
$fb = New-Object System.Drawing.SolidBrush $fill
|
||
$g.FillPath($fb, $path)
|
||
$p = New-Object System.Drawing.Pen $border, 1.6
|
||
$g.DrawPath($p, $path)
|
||
$fb.Dispose(); $p.Dispose(); $path.Dispose()
|
||
}
|
||
function Draw-Arrow($g, $x1, $y1, $x2, $y2, $color, $width = 2.4) {
|
||
$p = New-Object System.Drawing.Pen $color, $width
|
||
$p.CustomEndCap = New-Object System.Drawing.Drawing2D.AdjustableArrowCap 6, 7
|
||
$g.DrawLine($p, $x1, $y1, $x2, $y2)
|
||
$p.Dispose()
|
||
}
|
||
function Draw-Line($g, $x1, $y1, $x2, $y2, $color, $width = 2.4) {
|
||
$p = New-Object System.Drawing.Pen $color, $width
|
||
$g.DrawLine($p, $x1, $y1, $x2, $y2)
|
||
$p.Dispose()
|
||
}
|
||
function Save-Png($bmp, $g, $path) { $g.Dispose(); $bmp.Save($path, [System.Drawing.Imaging.ImageFormat]::Png); $bmp.Dispose() }
|
||
|
||
# ---------------------------------------------------------------- 图 1:插件做了什么
|
||
$r = New-Canvas; $bmp = $r[0]; $g = $r[1]
|
||
Draw-Text $g 'dsh-plugin-rea:把 REA 接入 DeepSeek Harness' $fTitle $ink 60 46
|
||
Draw-Text $g '第三方适配 · 自身不做逆向分析 · 自供给 Node/REA/JDK/Ghidra 后自动接好那一行' $fSub $muted 62 98
|
||
|
||
$rowY = 180; $boxH = 158; $boxW = 250
|
||
Draw-Box $g 60 $rowY $boxW $boxH $([System.Drawing.Color]::White) $line
|
||
Draw-Text $g 'DeepSeek Harness 会话' $fBox $ink 80 ($rowY + 18)
|
||
Draw-Text $g "mcp__rea__* 工具`n($reaPin 暴露 $reaTools)`n工具随会话注入`n按 DSH 权限策略执行" $fBody $muted 80 ($rowY + 50)
|
||
|
||
Draw-Box $g 360 $rowY $boxW $boxH $([System.Drawing.Color]::FromArgb(255,239,246,255)) $accent
|
||
Draw-Text $g '官方 MCP 客户端行' $fBox $accent 380 ($rowY + 18)
|
||
Draw-Text $g "@deepseek-ai/dsh-mcp-client`nserverName = rea / transport = stdio`ncommand = 当前 Node 可执行文件`nargs[0] = <DSH_HOME>/plugins/rea-bundle/`n bin/rea-shim.mjs" $fBody $muted 380 ($rowY + 50)
|
||
Draw-Text $g '↑ 本插件插入的就是这一行' $fSmall $accent 360 ($rowY + $boxH + 8)
|
||
|
||
Draw-Box $g 660 $rowY $boxW $boxH $([System.Drawing.Color]::White) $line
|
||
Draw-Text $g 'rea-shim(供给后镜像到此)' $fBox $ink 680 ($rowY + 18)
|
||
Draw-Text $g "读 bundle.json 里的本机真相`n导出 GHIDRA_INSTALL_DIR /`n JAVA_HOME / REA_*`n再拉起 bundle 内置的 REA" $fBody $muted 680 ($rowY + 50)
|
||
|
||
Draw-Box $g 960 $rowY $boxW $boxH $([System.Drawing.Color]::FromArgb(255,240,253,244)) $accent2
|
||
Draw-Text $g '分析引擎(供给或复用)' $fBox $accent2 980 ($rowY + 18)
|
||
Draw-Text $g "Ghidra 12.1.x / JDK 21:`n缺则下载并校验,`n已有则 --reuse 复用`n静态 JS / .NET 无需引擎" $fBody $muted 980 ($rowY + 50)
|
||
|
||
Draw-Arrow $g 310 ($rowY + 40) 358 ($rowY + 40) $muted
|
||
Draw-Arrow $g 610 ($rowY + 40) 658 ($rowY + 40) $muted
|
||
Draw-Arrow $g 910 ($rowY + 40) 958 ($rowY + 40) $muted
|
||
|
||
# Evidence 回传:引擎 → 结果框 → 会话(左侧折返)
|
||
Draw-Box $g 360 404 850 84 $([System.Drawing.Color]::FromArgb(255,254,249,195)) $([System.Drawing.Color]::FromArgb(255,202,138,4))
|
||
Draw-Text $g 'Evidence 回到会话(置信度 observed / derived / inferred + 明确的 limitations)' $fBox $ink 380 416
|
||
Draw-Text $g '反编译器输出的是伪代码与汇编 —— 不是原始源码,也不保证文本等价' $fBody $muted 380 448
|
||
Draw-Arrow $g 1085 340 1085 400 $muted
|
||
Draw-Line $g 358 446 150 446 $muted
|
||
Draw-Arrow $g 150 446 150 342 $muted
|
||
|
||
Draw-Text $g '上游 REA(MIT):github.com/morluto/rea 本插件:github.com/czcdr/dsh-plugin-rea' $fSmall $muted 60 620
|
||
Draw-Text $g "实测 REA $reaPin($reaTools 个工具) + DSH 桌面运行时 $hostMeasured + Ghidra $ghidraMeasured + Node ${nodePin}(Windows 11 x64,$measuredOn)" $fSmall $muted 60 646
|
||
Draw-Text $g 'Ghidra 为 Apache-2.0(NSA);REA 为 MIT。本仓库不分发上游代码。' $fSmall $muted 60 672
|
||
Save-Png $bmp $g (Join-Path $OutDir 'screenshot-1.png')
|
||
|
||
# ---------------------------------------------------------------- 图 2:范围与边界
|
||
$r = New-Canvas; $bmp = $r[0]; $g = $r[1]
|
||
Draw-Text $g '上游 REA 的分析范围(插件原样提供)' $fTitle $ink 60 44
|
||
Draw-Text $g '下列目标类型来自上游 README「可以分析什么」;本插件不改变、不扩展这些能力' $fSub $muted 62 96
|
||
|
||
$chips = @(
|
||
'原生二进制:伪代码/汇编/字符串/符号/调用与引用',
|
||
'JavaScript / Electron:模块、导入、source map、路由、IPC',
|
||
'.NET 程序集:元数据、CIL、原生依赖声明',
|
||
'网站:页面结构、脚本、网络观察、截图',
|
||
'Android APK:清单、类、反编译的方法、引用',
|
||
'Android 设备:adb 包/进程/日志/截图/文件传输',
|
||
'JEB 项目:单元、工件摘要、类型与方法伪代码',
|
||
'Android 资源:解码清单、字符串表、语言区域',
|
||
'固件:区域、提取结果、转交原生分析',
|
||
'软件包与资源:清单、摘要、plist、Apple bundle',
|
||
'进程行为:终端输出、交互、退出、文件系统观察',
|
||
'离线 ELF 布局:节、段、符号/重定位、防护候选项',
|
||
'EVM 字节码:分派选择器、字节偏移、可变性',
|
||
'已记录的 Linux 崩溃:note、线程寄存器、映射候选',
|
||
'已保存的网络捕获:HAR / mitmproxy 记录'
|
||
)
|
||
$cx = 60; $cy = 146; $cw = 570; $ch = 40; $gap = 8
|
||
for ($i = 0; $i -lt $chips.Count; $i++) {
|
||
$col = $i % 2; $rowi = [math]::Floor($i / 2)
|
||
$x = $cx + $col * ($cw + 20); $y = $cy + $rowi * ($ch + $gap)
|
||
Draw-Box $g $x $y $cw $ch $([System.Drawing.Color]::White) $line 8
|
||
Draw-Text $g $chips[$i] $fChip $ink ($x + 14) ($y + 9)
|
||
}
|
||
|
||
$by = $cy + 8 * ($ch + $gap) + 16
|
||
Draw-Box $g 60 $by 1190 92 $([System.Drawing.Color]::FromArgb(255,254,242,242)) $warn
|
||
Draw-Text $g '不能完整还原源代码' $fBox $warn 80 ($by + 10)
|
||
Draw-Text $g '返回的是伪代码/汇编/反编译方法,不是原始源码;标识符、类型、注释、宏与模板、构建脚本与资源多不可恢复;不保证文本等价或可直接编译。' $fBody $ink 80 ($by + 38)
|
||
Draw-Text $g '优化(内联/重排)、strip、混淆、加壳与 VM 保护会让可读性更差。要真正的源码,请走上游仓库、厂商授权或官方 SDK。' $fBody $muted 80 ($by + 60)
|
||
|
||
Draw-Text $g '上游 REA(MIT):github.com/morluto/rea 本插件:github.com/czcdr/dsh-plugin-rea' $fSmall $muted 60 686
|
||
Save-Png $bmp $g (Join-Path $OutDir 'screenshot-2.png')
|
||
|
||
Write-Host "written: $(Join-Path $OutDir 'screenshot-1.png')"
|
||
Write-Host "written: $(Join-Path $OutDir 'screenshot-2.png')"
|