mcpField
R
re-unpack-advanced

re-unpack-advanced

github.com/dslsdzcセキュリティ20659 回インストール2 週間前に更新
リポジトリ

re-unpack-advanced について

re-unpack-advancedは、VMProtectやThemidaなどで保護されたバイナリを扱うリバースエンジニア向けスキルです。デバッグ、メモリダンプ、IAT修復に加え、仮想化コードを完全に復元せずブラックボックスとして分析する方法も扱います。

主な機能

  1. 01多層のアンチデバッグ検知への対処
  2. 02元のエントリーポイントの特定
  3. 03メモリダンプとIAT修復
  4. 04仮想化コード領域のマーキング
  5. 05解析可能なアンパック状態の検証

活用シーン

  1. 01VMProtectやThemidaのサンプル分析
  2. 02静的解析用のクリーンサンプル準備
  3. 03保護バイナリのアンチデバッグ機構の調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dslsdzc/rev-skills --skill re-unpack-advanced

インストール後、Claude Code で/re-unpack-advancedを入力して読み込まれたか確認します。

詳細

アンパック:強力なプロテクター(VMProtect/Themida)

使用する場合/使用しない場合

  • 使用する:[[re-packer-id]] で強力なプロテクター/仮想化プロテクター(VMProtect、Themida、Enigma など)と識別された場合。シンプルなプロテクターの手動手順に失敗した場合(強力なプロテクターが重ねられている可能性があります)。複数層のアンチデバッグによって通常のデバッグが妨げられているサンプルの場合
  • 使用する:詳細な静的解析/クラック前の準備([[re-cracking]] の手順)にクリーンなサンプルが必要な場合
  • 使用しない:シンプルな圧縮プロテクターの場合([[re-unpack-simple]] ですぐにアンパックできるため、重い手順は使わないでください)
  • 使用しない:仮想化コードをブラックボックスとして注釈付けできる対象の場合(復元するよりも、バイパスを注釈付けするほうが時間を 10 分の 1 に抑えられます。落とし穴 2 を参照)
  • 注意:このスキルはコストが高く、失敗しやすいため、まず対象の価値と「注釈付けだけで復元しない」方法で十分かどうかを評価してください

ツールの準備

デバッガー(OS 別)
  • Linux/Wine での PE のデバッグ:[[re-gdb]] —— apt install gdb / dnf install gdb / pacman -S gdb;Wine で直接読み込む:wine sample.exe の後、gdb -p <pid> でアタッチします([[re-analyze/platform-tips]] の Linux 分岐を参照)
  • Windows:[[re-x64dbg]] —— 公式リリース zip。x64dbg は強力なプロテクターへのアタッチやブレークポイントのサポートに優れているため、強力なプロテクターを扱う場合は優先してください
  • WSL:Windows のプロセスにはアタッチできません。Windows 側のツールを使用してください([[re-analyze/platform-tips]] の WSL 分岐を参照)
  • 確認:gdb --version。x64dbg でサンプルを読み込み、ステップ実行できること
Scylla(IAT の修復、リダイレクト処理を含む)
  • 新しい x64dbg には内蔵されています。単体版は GitHub の NtQuery/Scylla リリースから入手できます
  • Wine 環境:単体版 Scylla で Wine プロセスにアタッチできます(失敗した場合は Windows 環境で実行してください)
  • 確認:x64dbg のプラグインメニューに Scylla が表示されること
scyllaHide(アンチデバッグ対策、GitHub)
  • GitHub の x64dbg/ScyllaHide リリース zip を入手し、x64dbg のプラグインディレクトリに配置して有効にします。リリースには Wine での読み込み方法も含まれています
  • 作用:デバッグの痕跡(NtQueryInformationProcess、ThreadHideFromDebugger、時間差、デバッグポートなど)を一括で隠します
  • 確認:x64dbg のプラグインメニューに ScyllaHide が表示され、オプションを選択すると対応する検知を隠せること
スクリプトツール(アンパックの補助/注釈付け)
  • idapython:IDA に付属しています([[re-ida]] のワークフロー)。確認:IDA 内の File > Script Command から Python を実行できること
  • rizin スクリプト:インストール手順は [[re-radare2]] を参照してください。インストール後、rizin -v で確認します

操作手順

順番に実行し、各手順の結果を記録します(証拠のパス+sha256。[[re-triage]] を参照)。

  1. アンチデバッグ対策(ScyllaHide の考え方):

    • 主な検出ポイント:NtQueryInformationProcess(ProcessDebugPort / ProcessDebugFlags / ProcessDebugObjectHandle)、NtSetInformationThread(ThreadHideFromDebugger)、時間差(RDTSC / GetTickCount / QueryPerformanceCounter)、デバッグポート(NtQuerySystemInformation)、ウィンドウ/プロセス名の列挙。
    • x64dbg + ScyllaHide:プラグインメニューですべてのオプションにチェックを入れ、対象を隠蔽状態で起動します。Wine では、release に含まれる wine 用の方法で読み込んでから attach します。
    • 最外層から攻める:検出は通常チェーン状になっているため、まず静的解析で最外層の検出ポイントを特定します([[re-ida]] / [[re-radare2]] で NtQueryInformationProcess の xref を探します)。ScyllaHide で一括して隠蔽し、残った個別のポイントをパッチします(落とし穴 1 を参照)。
  2. OEP の特定(スタックトレース/メモリブレークポイントの併用):

    • エントリーポイント ≠ 元のプログラムの論理エントリーポイント:仮想化パッカーにも「エントリーポイント」はあります。EP の読み込み後、制御フローはパッカーのコードに入り(Themida などでは VM ディスパッチャーに入ります)、このエントリーポイントはパッカーの読み込み/ディスパッチ地点にすぎず、元のプログラムの論理エントリーポイントではありません。「仮想化パッカーに OEP の概念はない」という説明は正確ではありません。元のエントリーポイントは今も存在しますが、パッカーによって VM インタープリター層に隠されたり、経由されたりしています(落とし穴「VM エントリーの特徴はビルドごとに変化する」を参照)。これを特定するのが本手順の目的です。
    • スタックトレース:強力なパッカーの stub は末尾の ret で OEP に戻ります。ステップ実行/ブレークポイントで ret に停止し、スタックトップのアドレスを OEP の手掛かりとして確認します(stub は複数のセグメントやスレッドにまたがることが多いため、何度かブレークしてパターンを見つけます)。
    • メモリブレークポイントの併用:bp VirtualAlloc(x64dbg。gdb/Wine では break *VirtualAlloc)→ 戻るたびに、割り当て領域に実行可能な内容が書き込まれたか確認します → その領域にメモリアクセスブレークポイントを設定します(View > Memory Map > 対象の節 > Set breakpoint on access)→「最後の復号書き込み」の後に停止させると、その近辺が OEP です。
    • OEP の特徴:関数プロローグ(push ebp; mov ebp,esp)+密集した通常の API 参照。OEP アドレスとイメージベースを記録します。
  3. ダンプ(デフォルトではダンプを優先):

    • Linux/Wine:OEP まで実行した後、gcore -o out <pid> を実行します(デフォルトではダンプを優先します。全手順は [[re-memdump]] を参照。ダンプ前に maps で vsyscall/vdso を除外します。タイミングについては [[re-analyze/platform-tips]] の重要な知見を参照)。
    • Windows:x64dbg で OEP に移動 → Scylla → Attach → OEP を入力 → Dump。
    • 比較検証のため、ダンプ前後にそれぞれ sha256 を保存します。
  4. IAT の修復(Scylla、リダイレクト処理を含む):

    • Plugins > Scylla > IAT Autosearch → Get Imports → "invalid" と表示されたインポートを一つずつ確認します。
    • リダイレクト処理:強力なパッカーは API 呼び出しをパッカーの stub(VM 内のジャンプ)に横取りします。VM stub を指す無効な項目については、リダイレクト関係(元の API → stub アドレス)を記録し、idapython / rizin スクリプトで stub を実際の API に対応付けて一括でラベル付けします。解析できない項目には印を付け、後続の分析で手動補完します(まず Get Imports で、通常の API の大部分が解決済みであることを確認します)。
    • IAT Autosearch に問題がある場合のインポート関係の復元(3 つの手掛かり):Autosearch で見つからない、または invalid が大量にある場合、Autosearch を再実行するだけでは不十分です。① API 呼び出しチェーン:実際の CALL [addr] 呼び出し箇所を特定し、アドレステーブルを逆算します。② 解決関数:パッカーの API 解決ループ(GetProcAddress の呼び出し箇所、または API hash の解決実装)を探し、メモリ書き込み箇所にブレークポイントを設定して、IAT 領域に書き込まれるアドレスを記録します。③ 強力なパッカーでは API hash/動的解決/syscall の直接実行が使われている可能性があります(修復可能な IAT がありません)。「ラベル付け」の手順に移り、呼び出し箇所の意味を記録します(手順 5 を参照)。「最初の API 呼び出し」を追跡する方法は汎用的ではありません。強力なパッカーでは、解決処理が複数スレッド/遅延フェーズで行われたり、syscall が直接実行されたりするため、追跡できる API 呼び出しがないことがあります。
    • 遅延インポート/bound import/API resolver:Scylla の遅延インポートには Delayed オプションを選択します(遅延インポートテーブルを処理します)。bound import はバインド時のタイムスタンプに基づいて判定し、実際のインポートに置き換えます。API resolver 型のパッカー(実行時に初めて解決するもの)では、API が実際に使用されるまで実行してから Get Imports を行い、早い段階で Fix Dump を実行しないでください。
    • Fix Dump で修復済みの exe を出力し、インポートテーブルを再度逆コンパイルして検証します([[re-ghidra]] / [[re-ida]])。
  5. 仮想化コード領域のラベル付け(復元ではなく、バイパスとしてマーク):

    • 目標=VM インタープリター層の複雑さを軽減すること:仮想化保護への対処目標は、VM インタープリター層の分析を簡素化することであり、「仮想化関数を元のコードに復元する」ことではありません。後者は VMProtect/Themida では実現可能な方法がありません。達成可能な目標は次の 2 つのいずれかです。VM の復元(重要な検証処理がごく少数の場合に限り、handler の意味を復元してロジックを書き換える)またはラベル付けによるバイパス(ブラックボックスとして入出力を観察する)。労力の評価は「復元度」ではなく「分析可能性」を基準にします。
    • VM エントリーを特定します。VMProtect の VM_Entry は push imm32; mov eax, imm; jmp vm_handler のような形式です(Themida も同様で、節名は .vmp0/.vmp1/.themida です)。
    • 復元ではなくラベル付け:仮想化関数をブラックボックスとしてマークします(エントリーアドレス、引数の数、呼び出し箇所の一覧を記録します)。以降の分析では、静的な復元の代わりに、動的に入出力を観察します([[re-gdb]] / [[re-x64dbg]] で VM エントリーにブレークし、引数と戻り値を記録します)。復元が本当に必要なごく少数の重要な検証処理(注册码の検証など)に限り、VM バイトコードのリバースに取り組みます。
    • 成果物:ラベル一覧 vm_blackbox.txt(アドレス → 説明)。分析レポートとともに保管します。

領域横断の連携

  • [[re-anti-analysis]]:ワークフローの手順 4(強力なパッカーの分岐)では、このスキルを必ず呼び出します。識別できないパッカーや、単純なパッカーの手順で失敗した未知のパッカーにも、この手順を適用します。
  • [[re-analyze]] のトリアージにおける「サンプルにパッカーあり/アンパック」経路から呼び出します。
  • [[re-malware]]:強力なパッカーが使われたマルウェアサンプル(VMProtect でパッキングされたものが一般的です)。
  • [[re-cracking]]:クラック前の準備として、強力なパッカーで保護されたものを先にアンパックし、その後ライセンス処理を特定します。
  • 関連ツール:[[re-memdump]](デフォルトのダンプ)、[[re-sandbox]](アンパック成果物を再実行して検証)、[[re-tracing]](アンチデバッグがトレース環境を検出する場合に併用)、[[re-ida]] / [[re-radare2]](検出ポイントと VM エントリーの静的な特定)

よくある落とし穴と注意点

  • 反调试链多层 → 先攻最外层:现象——scyllaHide 全开仍被踢,或过了一个检测又被下一个拦;原因——检测是链式的(外层过才触发内层);对策——逐层攻:先静态定位最外层检测点(xref NtQueryInformationProcess / 时间差函数),scyllaHide 批量隐藏常规项,剩余单点 patch 后继续
  • VM 代码还原成本高 → 标记而非还原:现象——陷在 VMProtect 字节码逆向里数天,进度停滞;原因——虚拟机指令集私有、还原工程量巨大;目标设错——原始代码不是虚拟化保护的可达产物;对策——目标=降低 VM 解释层复杂度(VM 还原 / 标注绕过两条路,见步骤 5),把虚拟化区域标为黑盒,动态观察输入输出(断 VM 入口记参数/返回值),只还原真正关键的小段(如注册码校验),按"可分析性"验收而非"还原度"
  • 多线程壳(监控线程)干扰:现象——调试中被踢、进程自毁、断点打上就被线程改掉;原因——壳起监控线程检测调试状态;对策——入口处挂起多余线程(x64dbg Threads 面板 Suspend;gdb info threads 后单线程 continue),只在目标线程内推进
  • 壳检测调试器环境:现象——一开调试器进程就退出 / 立即自毁;原因——检测调试端口、父进程名、窗口名(FindWindowA)、时间差;对策——ScyllaHide 隐藏 + 调试器进程改名 + 先静态 patch 检测点再 attach;仍在沙箱快照内操作([[re-sandbox]])
  • 硬件断点被壳检测:现象——ESP 定律的硬件访问断点不触发 / 一下断进程即退出;原因——新版 Themida 等检测硬件调试寄存器(DR0-3);对策——ScyllaHide 用注入方式隐藏(配套 HookLibraryx86.dll + InjectorCLIx86.exe 配置),或改用内存断点 / TitanHide,再不行先静态 patch 检测点再 attach
  • VM 入口特征随构建变异 → 别只靠签名:现象——按教程特征找 VM 入口 / dispatcher 定位失败,或还原产物错乱;原因——VMProtect 的 handler 每次构建都会变异(同 opcode 不同代码)、dispatcher 含 opaque predicate、多层 VM(VM 套 VM)叠加(见 [[re-deobfuscate]] 的 opaque predicate 处理);对策——先试自动化框架(x64Unpack 混合仿真 / DragonSlayer 符号执行+污点跟踪),手动时用动态 trace 记录 handler 执行序列,以"字节码指针寄存器 + 循环分发"定位而非固定签名
  • 带强壳名的样本未必启用虚拟化:现象——DIE 报 VMProtect/Themida 就上全套强壳流程,标准断点技巧其实就能脱出;原因——虚拟化/反调试是壳的配置选项,大量真实样本(RisePro/Amadey/PrivateLoader 等)未启用、无反调试;对策——先试常规流程(VirtualAlloc 断点 + 内存断点 + 单步),确认 VM 分发型入口确实存在再投入高成本还原
  • 把壳入口当原始入口 / 以为虚拟化壳"无入口":现象——在壳入口(VM dispatcher 分发点)找不到原始逻辑就判定"强壳无 OEP 概念"放弃,或在 dispatcher 里死挖原始代码;原因——混淆"壳入口"(EP 后进入的加载/分发点)与"原始程序逻辑入口"(被虚拟化藏起的原始 OEP);对策——壳入口是客观存在的(Themida 加载即进 dispatcher),用它下断/设日志观察初始化行为;原始逻辑入口按步骤 2 定位,虚拟化部分按步骤 5 处理
  • IAT 修复死磕 Autosearch:现象——IAT Autosearch 找不到 / 导入全 invalid,反复重跑仍无果;原因——强壳运行时重建导入表(API hash / 动态解析 / syscall 直通),静态无表可搜;对策——改用步骤 4 三线索(调用链 / 解析函数 / 内存写入点)恢复导入关系,无法恢复的项转标注;"跟踪第一个 API 调用"在强壳不是通用法(多线程 / 延迟解析 / syscall 直通时没有可跟的调用),别在首调上耗时间
  • 把"找到 OEP"当脱壳成功:现象——停在 OEP、转储、IAT 修复都做了,反编译结果仍不可用(代码段还是壳数据 / 重定位全乱 / 异常表指向壳代码 / 运行即崩),反复重脱无果;原因——把脱壳定义窄化为"找到 OEP";对策——脱壳=恢复可分析状态:代码段为真实指令、导入表可解析、重定位 / 异常表 / 加载配置与真实映像一致(逐项核对见 [[re-format-pe]]),验证通过才算完成,缺哪项修哪项
  • 反调试在入口断点前已跑(TLS 回调抢先):现象——在 EP 下断点/单步,反调试检测却已先触发(启动即退出或状态已被改过),入口断点根本没生效;原因——TLS 回调(TLS Callback)比 Entry Point 更早执行,壳把反调试藏进 TLS 回调;对策——先查 PE 的 TLS 目录([[re-format-pe]])列出回调地址,在回调入口下断 / 静态分析回调内逻辑,入口断点没触发反调试时优先查 TLS(见 [[re-anti-analysis]] 反调试方法论 AD18)
  • 反调试不退出而是给假逻辑:现象——过掉检测点后进程不退出,但返回错误数据 / 算法结果被改 / 关键逻辑被跳过 / 执行被延迟,脱壳产物行为诡异难定位;原因——反调试可能故意制造假逻辑(返回错误数据/修改算法结果/延迟执行/跳过关键逻辑),比直接退出更常见;对策——patch 检测点后别直接信任输出,对比"检测为真 / 为假"两条路径的结果差异,先确认检测结果如何影响控制流(退出/降功能/假数据/延迟)再决定改法(见 [[re-anti-analysis]] 反调试方法论 AD44)
  • 壳检测 VM 环境(VM 专有通道):现象——VM 里调试 / 跑脱壳流程时样本行为与预期不符(初始化路径不同、部分功能不触发、脱壳产物残缺),但看不出反调试迹象;原因——样本调用 VMware backdoor / Guest Additions / vmtools 等 VM 专有通道检测虚拟化环境——高概率虚拟化检测信号;对策——xref 这些调用点与 CPUID vendor leaf(0x40000000),确认检测结果如何影响控制流,必要时 patch 返回值为非 VM 后再走脱壳流程(见 [[re-anti-analysis]] 反虚拟化方法论 AV-007)
  • 检测到 VM 后给假逻辑 / 换分支(不只退出):现象——样本在 VM 环境不退出,但脱壳产物行为诡异(假数据 / 跳过关键逻辑 / 不同载荷),与宿主机对比行为不一致;原因——反虚拟化检测结果影响执行路径,VM 环境进入不同分支——反虚拟化不仅用于退出,可能改变样本行为;对策——分析 VM / 非 VM 两个分支差异,patch 检测点后对比两条路径输出再下结论,别直接信任脱壳产物(见 [[re-anti-analysis]] 反虚拟化方法论 AV-008)
  • IAT 伪装(白名单 API 噪音填充):现象——脱壳后导入表"修复正常"、反编译流畅,但静态分析结论与运行行为对不上(导入表里的 API 实际一个都没被调用);原因——恶意代码/壳往 IAT 注入白名单 API 噪音(IAT camouflage,2025 研究确认用于误导静态检测),让导入表看起来人畜无害;对策——xref 验证 CALL [IAT] 实际调用点而非轻信导入表,用 [[re-tracing]] / [[re-frida]] 对照运行期真实调用,装饰性导入单独标记排除再下结论
  • VMP 3.8+ 混淆策略升级(指令文本特征失效):现象——3.8 之前基于指令文本特征匹配 Handler 的分析插件全部失效;原因——VMProtect 3.8(源码泄露后重构)引入五类混淆:①全指令变形(除 XOR 解密指令外几乎所有指令有变体,含伪代码解密/读取指令/计算 Handler 地址的多种形式);②内存地址常数加密(凡含内存地址常数的操作数,常数被加密);③Handler 变形(计算虚拟寄存器地址的 Handler 被替换);④Handler 一级融合(相邻 Handler 合并,丢弃"计算下一条 Handler 地址"的代码块,直接跳转/直连);⑤Handler 二级融合(如"读加数→读被加数→执行 ADD"三条 Handler 合并,虚拟栈操作被丢弃、绑定寄存器直接 add eax,ecx);对策——分析插件改语义/数据流匹配而非指令文本;识别融合 Handler 时注意虚拟栈操作可能已被寄存器直连替代;3.5.1 源码公开 ≠ 3.8+ 可照搬(3.8 已重构)
  • VMP 3.96 dump 时机与 OEP 真假判断:现象——VMP 代码段刚转储就 dump 得到的是壳代码不是原程序;原因——.text 里混有 VMP 自己的代码,解密/解压完成前 dump 都是垃圾;对策——用内存属性状态机判断解密阶段:密文 RX →(VMP 要写明文先给写权限)→ 解密中可写(WRITECOPY/RWX,关键信号)→ 解密完成回到 RX(即将 jmp OEP,此时才是 dump 时机);VMP 外壳加载流程:Loader 自举(算 delta、自解密、走 PEB 找 LoadLibrary/GetProcAddress)→ 环境检查(反调试/反 VT)→ 解包原始镜像 → 修重定位 → 重建 IAT(多态 stub)→ 初始化 VM 引擎 → 恢复内存保护属性(VMP 用 syscall 50 恢复,内核方案可在此下断)→ jmp OEP
  • VEH + Instrumentation Callback 拉锯 dump(三环方案):现象——VMP 检测调试器附加(syscall 规避 hook、检测 VM、检测测试模式),内核 dump 复杂;原因——附加调试器本身会被检测;对策——DLL 注入 + 异常接管,完全暴力规避附加:把 .text 设成不可执行触发 DEP 执行违规 → VEH 接管(只认执行违规 ExceptionInformation[0]==8 且地址在 .text 内,此时 RIP 就是真 OEP);对抗 VMP 扫描:VMP 会修复 .text 属性并抹掉 VEH 回调 → watchdog 线程定期重装回调(NtSetInformationProcess 0x28 ProcessInstrumentationCallback)+ Instrumentation Callback 在每次 syscall 返回时把 .text 剥回 RW(拉锯战);布防时机用 LATE_ARM:见过可写(解密发生)且当前可执行不可写(单看可执行会把初始密文态误判成时机)
  • 内存快照直接当文件(三要素):现象——dump 的内存无法用反编译器加载(对齐/入口/段偏移全乱);原因——内存布局与文件布局不同;对策——dump 后修 PE:①EntryPoint 改成 OEP;②FileAlignment = SectionAlignment(文件对齐=内存对齐);③每节 PointerToRawData = VirtualAddress、SizeOfRawData = align(VirtualSize)(raw 偏移直接等于虚拟偏移,文件第 X 字节 = 内存 RVA X);再导出全部依赖模块表便于修复 IAT

コメント