re-unpack-simple について
UPX、ASPack、FSG などの単純な圧縮シェルを対象とするリバースエンジニアリング用スキルです。自動・デバッガーによる展開からインポート修復、サンドボックスでの検証まで扱い、複雑なシェルは高度な手順に切り分けます。
主な機能
- 01UPXの自動展開
- 02ESP定理によるOEP特定
- 03メモリブレークポイントでの展開
- 04ダンプとIAT修復
- 05サンドボックスでの検証
活用シーン
- 01静的解析用にサンプルを準備
- 02自動展開に失敗した際の手動処理
- 03ダンプ後のインポート修復
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ dslsdzc/ rev-skills --skill re-unpack-simpleインストール後、Claude Code で/re-unpack-simpleを入力して読み込まれたか確認します。
詳細
アンパック:圧縮パッカー(UPX/ASPack/FSG)
使用する場合/使用しない場合
- 使用する:[[re-packer-id]] で単純な圧縮パッカー(UPX/ASPack/FSG など)と識別された場合。パッカーのエントリポイントに
pushad/pushaの特徴がある場合。静的/動的解析を続けるためにクリーンなサンプルが必要な場合 - 使用する:未知のパッカーを手動で解析する際の最初の試行(ESP の法則は大半の圧縮パッカーに有効です)
- 使用しない:強力なパッカー/仮想化パッカー(VMProtect/Themida。[[re-unpack-advanced]] に進んでください)
- 使用しない:対象にパッカーがないことが確認済みの場合(そのまま [[re-binary-core]] に進んでください)
- 使用しない:パッカーが適用された状態の動作を観察する必要がある場合(動作解析では、意図的にパッカーを残すことがあります。[[re-behavior]] と併用する場合は判断してください)
- 注意:アンパック後の成果物の検証は必ずサンドボックス内で行ってください([[re-sandbox]]、[[re-analyze/platform-tips]] の最優先原則)
ツールの準備
upx(公式のアンパックツール)
- Linux:
apt install upx-ucl/dnf install upx/pacman -S upx - macOS:
brew install upx - Windows:公式 GitHub release
upx/upxから win64 zip をダウンロード(またはchoco install upx) - WSL:Linux パッケージ
- 確認:
upx --version
デバッガー(OS 別)
- Linux / Wine で PE をデバッグする場合:[[re-gdb]] ——
apt install gdb/dnf install gdb/pacman -S gdbを実行し、gdb --versionで確認します。Wine を直接読む場合:wine sample.exeの実行後にgdb -p <pid>でアタッチし、Wine プロセスを直接操作します([[re-analyze/platform-tips]] の Linux 分岐を参照) - Windows:[[re-x64dbg]] —— 公式 release zip を使用し、サンプルを読み込んでステップ実行できることを確認します
- macOS:PE のアンパックでは Wine + [[re-gdb]] または Windows VM を使用し、ホストのデバッガーは使用しません
- WSL:Windows プロセスにはアタッチできません。境界をまたぐ場合は Windows 側のツールを使用してください([[re-analyze/platform-tips]] の WSL 分岐)
Scylla(Windows の IAT 修復)
- x64dbg の新しい公式 release には同梱されています(plugins/ ディレクトリ内に Scylla があります)
- 単体版:GitHub
NtQuery/Scyllarelease - Wine 環境:単体版 Scylla で Wine プロセスにアタッチし、ダンプを修復できます(失敗した場合は Windows 環境で IAT を修復してください)
- 確認:x64dbg のプラグインメニューに Scylla が表示され、プロセスにアタッチしてモジュール一覧を表示できること
操作手順
順番に実行し、各手順の結果を記録してください(証拠のパス + sha256。[[re-triage]] を参照)。作業前にまず [[re-triage]] に従い、アンパック前のベースライン5項目(SHA256 / PE timestamp / EP RVA / section hash / import table)を記録し、アンパック後に項目ごとに照合して成否を判定してください。
-
公式/自動アンパックを優先する(
upx -d):upx -d sample.exe -o unpacked.exe file unpacked.exe # 应显示正常 PE 结构;仍报 UPX / 报错 → 失败 sha256sum unpacked.exeupx -dが失敗する一般的な原因:ファイルが変更されている(自己検証/二重パッキング)、UPX のバージョンに互換性がない。失敗した場合は手順 2 の手動プロセスに進みます。 -
ESP の法則で OEP を特定する(pushad の後にブレークポイントを設定):
- 原理:圧縮パッカーのエントリポイントで
pushadがレジスターを保存し、パッカーの復号が完了するとpopadが実行され、その後jmp OEPでジャンプします。pushad 実行後の ESP が指すアドレスにハードウェアアクセスブレークポイントを設定し、実行後、popadの直後にある最初の命令付近で OEP を探します。 - 本質(手法であり、アンパックの公式ではありません):エントリポイントでの
pushadによるスタックへの退避は、パッカーが「実行状態を保存」するために行う必然的な動作です。ESP の法則ではこれを利用して「実行状態の復元」地点を特定します。popadによる復元後は復号が完了しており、制御フローは実際のコードにジャンプします。この方法が成り立つのは「エントリポイントでスタックに退避し、末尾で復元してからジャンプする」という実装の場合に限られ、あらゆるパッカーに有効なわけではありません。TLS コールバックを使うパッカー(コールバックは EP より先に実行され、EP には退避された状態がない)、マルチスレッドのパッカー(各スレッドがそれぞれスタックに退避するため、ブレークポイントが別のスタックアドレスにヒットする)、VMProtect/Themida などの仮想化パッカー(エントリポイントが直接 VM dispatcher に入り、pushad/popad のサイクルがない)には適用できません。まず EP のエントリポイントが実際に pushad/popad のサイクルになっていることを確認してから使用してください。そうでない場合は、手順 3 に進むか [[re-unpack-advanced]] を参照してください。 - x64dbg(Windows):読み込み → EP で停止 → ステップ実行して
pushadを確認 → さらにステップ実行してpushadを実行 → pushad 実行後の ESP の値を記録 → ESP レジスターを右クリック → Hardware Breakpoint(on access)→ 実行 →popad直後の最初の命令で停止 → その下にある数行のjmpのジャンプ先が OEP です。OEP アドレスを記録してください。 - gdb(Linux/Wine。[[re-analyze/platform-tips]] の Wine 直接読み込みを参照):
(gdb) starti # EP で停止 (gdb) si # pushad をステップ実行(x86 pushad は 8 個のレジスターをスタックにプッシュ) (gdb) awatch *(int*)$esp # pushad 後の ESP が指すアドレスにハードウェアウォッチポイントを設定(アクセス時に停止) (gdb) continue # 実行し、popad がそのアドレスにアクセスした時点で停止 - OEP の特徴:関数プロローグ(
push ebp; mov ebp,esp)があり、その後に大量の通常の API 参照が続きます。
- 原理:圧縮パッカーのエントリポイントで
-
メモリブレークポイント法(VirtualAlloc ブレークポイント):
- 適用対象:ESP の法則が使えない場合(pushad がない、複数の stub、多段階の復号)。
- 考え方(復号領域の実行を監視する):パッカーは復号完了後の実際のコードを、ある領域(解凍先セクション/VirtualAlloc の割り当て領域)に書き込みます。その領域に実行/アクセス監視を設定し、パッカーの制御フローが初めて復号済みコードに到達した時点で停止します。つまり、復号完了後の実際のコードのエントリポイントで停止するため、「OEP がどのアドレスにあるか」を推測する必要はありません。
- 「最初に実行された場所が OEP」とは限りません:一部のパッカーは最初に loader(パッカー自身のランタイム)へジャンプします。多段階で復号する場合(最初に実行された場所は中間層で、後続の復号ループが残っている)、または fake OEP を配置する場合(偽の関数プロローグを置いて、早すぎるダンプを誘う)もあります。停止後は、対象に実際のコードの特徴(関数プロローグ
push ebp; mov ebp,espと、その後の高密度な通常の API 参照)があることを必ず確認してください。多段階パッカーの場合は、制御フローがパッカーのコードから安定して離れるまでブレークポイントを繰り返し設定してから、手順 4 に進みます。 - x64dbg:
bp VirtualAlloc→ 呼び出しから戻るたびに、割り当て領域に実行可能な内容(パッカーの解凍先セクション)が書き込まれているか確認 → その領域にメモリアクセスブレークポイントを設定(View > Memory Map > 対象セクション > Set breakpoint on access)→ パッカーの復号完了まで実行します。パッカーの第 2 セクション(UPX1/.aspack)に直接メモリブレークポイントを設定する方法もあります。 - gdb(Wine):
break *VirtualAllocを設定し、戻った後に割り当て領域を確認してから、割り当てアドレスに watch を設定します。
-
OEP をダンプする(タイミング:復号完了後。[[re-analyze/platform-tips]] の重要な知見を参照。既定ではダンプを優先):
- Linux/Wine:OEP(復号完了)まで実行した後、既定では
gcore -o out <pid>でダンプします(全手順は [[re-memdump]] を参照。ダンプ前に maps を使って vsyscall/vdso を除外してください)。 - Windows:x64dbg で OEP まで実行 →
Plugins > Scylla > Attach to process→ OEP アドレス(手順 2 で記録した値)を入力 →Dump。先にイメージベースアドレス + OEP オフセットを記録してください。 - パッカーの復号が完了する前にダンプしないでください——得られるのはパッカーの初期状態です(落とし穴 1 を参照)。
- Linux/Wine:OEP(復号完了)まで実行した後、既定では
-
IAT を修復する(Scylla/ImpREC):
- x64dbg:
Plugins > Scylla > Attach to process→ OEP に手順 2 のアドレスを入力 →IAT Autosearch→Get Imports(API が正しく解析されていることを確認)→Fix Dumpでunpacked_fixed.exeを出力します。 - 代替手段:ImpREC(Windows の旧ツール)も同じ手順です:Open Process → IAT AutoSearch → Fix Dump。
- 修復後の検証:
file unpacked_fixed.exe→ [[re-ghidra]] / [[re-ida]] にインポートして逆コンパイルし、インポートテーブルが解析可能であることを確認 → サンドボックス内で再実行([[re-sandbox]])し、アンパック前と動作が一致すること、自己検証が発動しないことを確認します。
- x64dbg:
跨域联合
- [[re-anti-analysis]]:工作流第 3 步(简单壳分支)固定调用本技能
- [[re-analyze]] 的 triage「样本带壳 / 脱壳」路径调用(re-anti-analysis → re-unpack-simple)
- [[re-malware]]:恶意样本加壳(UPX 等)脱壳后回沙箱复跑再行为分析
- [[re-mobile]] / [[re-apk]]:部分 Android 加固壳可类比本流程思路(具体加固壳按 [[re-frida]] / [[re-memdump]] 内存 DEX 提取)
- [[re-crypto-decrypt]] / [[re-crypto-keys]]:解密 / 密钥在壳内时先脱壳(OEP 后再 dump,见 [[re-memdump]] 转储时机)
- 配套:[[re-memdump]](默认转储)、[[re-gdb]] / [[re-x64dbg]](调试器)、[[re-sandbox]](脱壳产物复跑验证)
常见坑与陷阱
- 转储过早 = 壳初始状态:现象——dump 后
file仍报 UPX、反编译只见压缩数据;原因——壳未运行到 OEP 解密完成就转储;对策——解密完成后再 dump([[re-analyze/platform-tips]] 关键经验),默认转储优先 - IAT 不修 → 导入表乱:现象——脱壳样本反编译里全是
GetProcAddress动态调用、导入表解析失败;原因——导入表由壳在运行时重建,转储未修复;对策——步骤 5 Scylla/ImpREC 修复,修复后重新反编译验证 - 自校验(CRC)→ 脱壳后需补丁:现象——脱壳样本沙箱运行即退出 / 弹校验失败;原因——程序比对自身字节(原壳时是压缩数据,脱壳后对不上);对策——定位校验代码(xref 自身映像基址 / CRC 计算函数)patch 跳过或 hook,验证在沙箱内做
- Wine 环境用错调试器:现象——Windows 调试器在 Wine 下 attach 失败 / 断点不生效;原因——Wine 是 Linux 进程,需 Linux 调试路径;对策——按 [[re-analyze/platform-tips]] Wine 直读:
gdb -pattach Wine 进程读内存、下断点、gcore转储 - 双层压缩壳 → ESP 定律要用两次:现象——ESP 定律断下后进入的仍是解包层,dump 出来还是壳代码,
upx -d解一层后file仍报壳特征;原因——壳套壳(如 ASPack v2.12 第一层解完还有一层在解密 IAT);对策——第一层落地后继续观察:出现又一次 pushad/popad 周期、或VirtualFree释放 IAT 解密堆时才是最后一步,跟到最终jmp并验证目标是真实代码序言(push ebp; mov ebp,esp+ 密集正常 API 引用)再 dump,别在过渡 jmp 前早一跳 - 按 FF 25 搜 IAT 不可靠:现象——按教程二进制搜索
FF 25(间接 jmp)找 IAT 起址,找不到或找到错位置;原因——不是所有程序都经间接跳转调 API(直接CALL [addr]很常见);对策——用调试器Find > All intermodular calls定位一个真实程序调用点,顺CALL/JMP [addr]跟到跳转表顶端计算 IAT 起址与块大小,再填 ImportREC/Scylla - dump 后 PE 头字段未修 → 加载失败:现象——IAT 修复完样本仍打不开 / "invalid Win32 application" / 加载即崩;原因——转储工具没修
SizeOfImage/NumberOfSections/SizeOfHeaders,或节表各节的 RVA/raw 范围与 SectionAlignment/FileAlignment 不匹配,或 Win7+ 上 LordPE/ImportREC 因 ASLR 失败;对策——Fix Dump 后仍异常就用 pefile 逐项核对修正这些字段,必要时关闭随机基址后重新转储。CheckSum不属此类——普通用户态 EXE/DLL 加载期不校验,仅驱动、boot 期加载的 DLL 与载入关键系统进程的 DLL 需重算 - 转储后崩溃 ≠ 脱壳失败:现象——IAT 修复完成、反编译也正常,运行仍崩溃/闪退,误判脱壳失败而放弃;原因——除 IAT 外还依赖其他机制:TLS callback(壳在回调里重设参数/解密数据)、重定位表(ASLR 下基址重定位未处理)、异常表(SEH 链被壳重排)、Load Config(安全 cookie / GS 检查指向壳数据);对策——按 [[re-format-pe]] 逐项核对 TLS 回调表、重定位目录、异常目录、Load Config 目录,缺哪项修哪项;脱壳成功判据=代码/导入/重定位/异常机制完整(见 [[re-unpack-advanced]]),不是"不崩溃"或"修完 IAT"
- ESP 定律当公式硬套:现象——对 TLS 回调壳 / 多线程壳 / VMProtect 套 ESP 定律,硬件断点不触发或停在错位栈地址,反复试数小时无果;原因——把"入口压栈找恢复点"当通用公式,实际只适用于入口 pushad 的单线程压缩壳;对策——动手前确认适用前提(EP 入口确为 pushad/popad 周期),否则直接走步骤 3 内存断点法 / 转 [[re-unpack-advanced]],别在公式上耗时间
- 把"第一次执行解密区域"当 OEP 直接转储:现象——内存断点第一次触发就 dump,拿到的是壳 loader / fake OEP,反编译仍是壳代码;原因——壳先跳 loader、多阶段解密或布置 fake OEP,第一次执行≠解密完成;对策——断下后先验证真实代码特征(函数序言 + 密集 API 引用),多阶段壳重复下断直到控制流稳定
- Wine 脱壳失败未必是流程错:现象——Wine 下按完整流程操作,断点不触发 / 壳运行异常 / 解密结果错乱;原因——Wine 环境问题而非流程问题:缺失壳依赖的 API、壳驱动组件无法加载、反虚拟化检测(Wine 特征 API/注册表/进程名)或环境检测(系统版本/硬件特征)使壳拒绝正常解密;对策——先定位失败点:
WINEDEBUG=+loaddll看加载失败日志、断GetVersionEx/注册表读取看是否命中环境检测;确认是 Wine 环境限制后果断转 Windows VM([[re-sandbox]])完成脱壳,不在 Wine 里反复折腾
コメント
Markdown 対応