最終更新: 2026年9月16日

PPTX Reverse Engineering: Understanding PowerPoint Files Internally

リバースエンジニアリング PPTX ファイル:開発者ガイド

最新のプレゼンテーションデッキは、投資家向けピッチから社内の四半期指標まで、あらゆるものを支えています。しかし、テキストをプログラムで抽出したり、実行時にテンプレートを置き換えたり、自動スライドジェネレータを構築したり、機密プレゼンテーションをサニタイズしたりしたことがあるなら、すぐに気付くでしょう:標準的なハイレベルのプレゼンテーションライブラリは、予測不可能なブラックボックスのように感じられることがあります。

ライブラリ(python-pptxApache POIOpenXML SDK)が限界に達したり、未文書化のレイアウトバグを引き起こしたりすると、唯一の抜け道は内部に入ることです。PowerPoint プレゼンテーションがバイトレベルおよびスキーマレベルで実際に何であるかを理解する必要があります。

この徹底的な分析では、.pptx フォーマットのカーテンをめくり、その内部構造を解体し、リレーションシップグラフを追跡し、描画階層を分解し、そして生コードでプレゼンテーションをリバースエンジニアリング、検査、操作するための実践的な戦略を検討します。

1. .pptx ファイルとは実際に何ですか?

本質的に、.pptx ファイルは 1990 年代の古い .ppt フォーマットのような独自のモノリシックバイナリではありません。Microsoft が Office Open XML(ECMA-376 および ISO/IEC 29500)を導入して以来、最新の Office ドキュメントは Open Packaging Conventions (OPC) アーカイブ です。

簡単に言えば、.pptx ファイルは単に XML ドキュメントとメディア資産を含む zip アーカイブで、決定的なディレクトリツリーに整理されています。

標準的なターミナルツールを使えば、数秒でこれを証明できます:

# Rename the extension and unpack it
cp presentation.pptx presentation.zip
unzip presentation.zip -d presentation_unpacked/
cd presentation_unpacked/
tree -L 2

生成されるディレクトリツリーは驚くほど一貫しています:

.
├── [Content_Types].xml
├── _rels/
│   └── .rels
├── docProps/
│   ├── app.xml
│   └── core.xml
└── ppt/
    ├── presentation.xml
    ├── _rels/
    ├── slides/
    ├── slideLayouts/
    ├── slideMasters/
    ├── theme/
    └── media/

すべてのビジュアル資産、トランジション、スライドマスタの継承、テキストボックスの座標、ベクターグラフィックはすべてこのファイル階層にコード化されています。

2. パッケージの構造: 主要サブシステム

プレゼンテーションを効果的にリバースエンジニアリングするには、各トップレベルコンポーネントの役割を理解する必要があります。

[Content_Types].xml

これは OPC リーダー用のエントリマニフェストです。ファイル拡張子と明示的な内部パート名を標準化された MIME/コンテンツタイプにマッピングします。新しいスライドを作成したり画像を追加したりして、[Content_Types].xml に宣言し忘れると、PowerPoint はデッキが破損していると判断し、復旧を促します。

例のスニペット:

<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
  <Default Extension="xml" ContentType="application/xml"/>
  <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
  <Default Extension="png" ContentType="image/png"/>
  <Override PartName="/ppt/presentation.xml" 
            ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/>
</Types>

_rels/ リレーションシップ グラフ

OpenXMLで最も重要な概念の一つはリレーションシップによる間接参照です。パーツは生のファイルパスを直接指すことはほとんどありません。その代わり、ファイル(例:slide1.xml)には、同じ階層の _rels フォルダーにある関連リレーションシップファイル(例:_rels/slide1.xml.rels)が紐付けられています。

リレーションシップは次のものを定義します:

  • ハイパーリンク(r:id=\"rId2\"、TargetMode="External")
  • ppt/media/ に保存されている画像と音声トラック
  • レイアウト継承ポインタ(slideLayout1.xml
  • ノートスライド、コメント、埋め込みフォント

docProps/(メタデータ)

  • core.xml:Dublin Core メタデータ—作者、タイトル、作成日、変更タイムスタンプ。
  • app.xml:アプリケーション固有の統計情報—PowerPoint バージョン、総スライド数、非表示スライド数、単語数、プレゼンテーションテンプレート名。

ppt/(プレゼンテーション エンジン)

実際のプレゼンテーションがここにあります:

  • presentation.xml: マスタースパインです。スライドID、スライドサイズの寸法、ノートマスタ参照、デフォルトフォント設定を登録します。
  • slides/: 個々のスライド(slide1.xmlslide2.xml など)。
  • slideLayouts/: マスタ構造プリセット(タイトルスライド、2カラム、セクションヘッダー)。
  • slideMasters/: グローバルスタイル、デフォルトパレット、背景塗りつぶし、プレースホルダーの継承。
  • theme/: カラーパレット(アクセント1〜6、暗色/明色バリエーション)とフォントスキーム(メジャー/マイナーフォント)。
  • media/: 生画像(PNG、JPEG、SVG)、オーディオ、ビデオファイル。

3. スライドの解剖: PresentationML (p:) と DrawingML (a:) の方言

ppt/slides/slide1.xml を開くと、2つの主要なXML名前空間に出会います:

  1. PresentationML (p:): 構造的なプレゼンテーション要素(スライド、シェイプツリー、キャンバスグループ)を管理します。
  2. DrawingML (a:): タイポグラフィ、ジオメトリ、2D座標、グラデーション塗り、ベクター描画を管理します。

標準的なテキストシェイプがどのようなものか、簡略化した例は次のとおりです:

<p:sp>
  <!-- 1. Non-visual shape properties (Identifiers, names) -->
  <p:nvSpPr>
    <p:cNvPr id="4" name="Title Box 1"/>
    <p:cNvSpPr>
      <a:spLocks noGrp="1"/>
    </p:cNvSpPr>
    <p:nvPr>
      <p:ph type="title"/>
    </p:nvPr>
  </p:nvSpPr>

  <!-- 2. Visual shape properties (Position, size, geometry) -->
  <p:spPr>
    <a:xfrm>
      <a:off x="1524000" y="1143000"/>
      <a:ext cx="9144000" cy="1828800"/>
    </a:xfrm>
    <a:prstGeom prst="rect">
      <a:avLst/>
    </a:prstGeom>
    <a:solidFill>
      <a:schemeClr val="accent1"/>
    </a:solidFill>
  </p:spPr>

  <!-- 3. Text Body (Paragraphs, runs, styling) -->
  <p:txBody>
    <a:bodyPr rtlCol="0" anchor="ctr"/>
    <a:lstStyle/>
    <a:p>
      <a:r>
        <a:rPr lang="en-US" sz="3200" b="1"/>
        <a:t>Mission Critical Architecture</a:t>
      </a:r>
    </a:p>
  </p:txBody>
</p:sp>

重要な測定単位: EMU とポイントの百分の一

<a:xfrm> の座標番号に注目してください:

  • x="1524000"
  • cx="9144000"

これらは English Metric Units (EMUs) です。

  • $1 \text{ inch} = 914,400 \text{ EMUs}$
  • $1 \text{ cm} = 360,000 \text{ EMUs}$
  • $1 \text{ pt} = 12,700 \text{ EMUs}$

EMUs は、整数を使用してインチとミリメートルの両方の正確な分数を表現でき、異なるハードウェアアーキテクチャ間での浮動小数点丸め誤差を防ぎます。

フォントサイズにも注目してください:

  • sz="3200"32.00 pt を意味します。DrawingML のフォントサイズはポイントの百分の一単位で測定されます。

4. 継承チェーン: シェイプが不可視スタイルを継承する理由

PPTX ファイルをリバースエンジニアリングする際の最も一般的な落とし穴の一つは、シェイプの視覚的スタイリングがその独自の slideX.xml 内に完全に宣言されていると想定することです。

実際には、OpenXML は厳密な 4-tier cascading inheritance model に依存しています:

[Theme: ppt/theme/theme1.xml]
[Slide Master: ppt/slideMasters/slideMaster1.xml]
[Slide Layout: ppt/slideLayouts/slideLayout1.xml]
[Slide: ppt/slides/slide1.xml]

slide1.xml 上のテキストボックスに次のように含まれている場合:

<a:p>
  <a:r>
    <a:t>Revenue Projections</a:t>
  </a:r>
</a:p>

ラン(<a:r>)にはフォントファミリーも明示的な色もサイズも宣言されていません。PowerPoint がこのテキストをどのようにレンダリングするかを把握するには、パーサーは次のことを行う必要があります:

  1. プレースホルダーのタイプ(<p:ph type="title"/>)を特定する。
  2. slide1.xml.relsで参照されているレイアウトを読み取る。
  3. slideLayout1.xmlがそのプレースホルダーに対してスタイルのオーバーライドを提供しているか確認する。
  4. デフォルトのタイトルテキスト本文スタイルについては、slideMaster1.xmlにフォールバックする。
  5. accent1tx1といったカラー トークンをtheme1.xmlでたどり、十六進カラーコードを見つける。

この継承グラフを省略すると、パーサーはスタイルを誤って読み取り、フォントやフォントサイズ、レイアウトアンカーが欠落します。

5. 実践的リバースエンジニアリングワークフロー

特定の PowerPoint 機能が内部でどのように動作するか(例:モーフ遷移、複雑なテーブル、ベクトルパス)を調査する必要がある場合は、次の実証的アプローチに従ってください:

ステップ 1: 最小限の"Diff Pair"を作成する

  1. PowerPoint を開き、空白のスライドを作成します。
  2. before.pptx として保存します。
  3. 逆エンジニアリングしたい正確な単一の変更を適用します(例:円にドロップシャドウを追加、箇条書きスタイルを変更、埋め込み動画を挿入)。
  4. after.pptx として保存します。

ステップ 2: 両方のアーカイブを解凍する

unzip before.pptx -d before/
unzip after.pptx -d after/

ステップ 3: XML を整形する

Office アーカイブ内の生の XML は通常、インデントや改行文字が除去されています。差分を取る前に、ファイルを整形してください:

find before/ after/ -name "*.xml" -exec xmllint --format {} --output {} \;

ステップ 4: ユニファイド Diff を実行する

diff -uNr before/ after/ > changes.patch

changes.patch を確認すると、PowerPoint が導入した正確なタグ名、名前空間属性、コンテナ階層が分かります。これは、ECMA-376 の何千ページものドキュメントを掘り下げることなく、未文書化または曖昧なスキーマプロパティを発見する最速の方法です。

6. カスタムマイクロエンジンの構築: 解凍、変更、再パック

場合によっては、Apache POI や Microsoft OpenXML SDK のような重いエンタープライズ依存関係を使用したくないことがあります—特に軽量なサーバーレスランタイム(AWS Lambda、Cloudflare Workers、エッジノード)では。

以下は、メモリ上の PPTX を安全に解凍し、標準ライブラリツールを使用してカスタムデータを注入し、再パッケージ化する方法を示す自己完結型の Python パターンです:

import zipfile
import io
import xml.etree.ElementTree as ET

def modify_slide_title(input_pptx_bytes: bytes, new_title: str) -> bytes:
    input_zip = zipfile.ZipFile(io.BytesIO(input_pptx_bytes))
    output_buffer = io.BytesIO()
    
    with zipfile.ZipFile(output_buffer, "w", zipfile.ZIP_DEFLATED) as output_zip:
        for item in input_zip.infolist():
            content = input_zip.read(item.filename)
            
            # Target slide 1
            if item.filename == "ppt/slides/slide1.xml":
                namespaces = {
                    'p': 'http://schemas.openxmlformats.org/presentationml/2006/main',
                    'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'
                }
                
                # Register namespaces to preserve prefixes
                for prefix, uri in namespaces.items():
                    ET.register_namespace(prefix, uri)
                    
                root = ET.fromstring(content)
                
                # Find title placeholder text run
                for title_run in root.findall(".//p:sp[p:nvSpPr/p:nvPr/p:ph[@type='title']]//a:t", namespaces):
                    title_run.text = new_title
                    break
                
                content = ET.tostring(root, encoding="utf-8", xml_declaration=True)
            
            output_zip.writestr(item, content)
            
    return output_buffer.getvalue()

生バイトレベルでファイルを変更する際の重要な注意点:

  1. Namespace Preservation: XML パーサはしばしばプレフィックスを書き換えます(xmlns:pxmlns:ns0 になることがあります)。有効な XML ですが、PowerPoint の厳格な内部スキーマ検証は時折、標準外のプレフィックスエイリアスを拒否します。名前空間は明示的に登録してください。
  2. ZIP Compression Methods: 標準のデフレート圧縮(zipfile.ZIP_DEFLATED)を使用してファイルを書き戻すようにしてください。
  3. Stream Flushing: バイトを下流に送信する前に、zip バッファが完全に閉じられ、フラッシュされていることを必ず確認してください。
  4. Preserve Relationships: スライドを削除する場合、ppt/presentation.xml からそのエントリを削除し、ppt/_rels/presentation.xml.rels のリレーションシップも削除し、[Content_Types].xml からそのコンテンツタイプも除去する必要があります。

7. パフォーマンスとセキュリティの考慮事項

PPTX ファイルのリバースエンジニアリングは、スライドの編集だけでなく、システムに取り込まれるものを監査することでもあります。

セキュリティ: ビリオン・ラフス & XXE

PPTX ファイルは XML を解析するため、ユーザーが提出したプレゼンテーションを取り込むサーバー側パイプラインは以下の脅威にさらされます:

  • XML External Entity (XXE) Injection: /etc/passwd へのアクセスや内部クラウドメタデータエンドポイント(http://169.254.169.254/)へのクエリを試みる悪意ある XML。
  • Entity Expansion Attacks (Billion Laughs): システムの RAM を使い果たす指数的なエンティティループ。

緩和策: 常に XML パーサーで resolve_entitiesload_dtd、および外部ネットワーク解決を無効にしてください(例: Python の defusedxml を使用)。

セキュリティ: マクロペイロードと隠れたストリーム

.pptm コンテンツが .pptx 拡張子で偽装していないかファイルを検査してください。コンパイルされた Visual Basic コードを含む ppt/vbaProject.bin に注意してください。標準の .pptx ファイルでは VBA コードは禁止されており、リレーションシップ内でバイナリペイロードの参照が見つかった場合は直ちに隔離フラグを発動すべきです。

結論

.pptx ファイルのリバースエンジニアリングはプレゼンテーションソフトウェアの謎を解き明かします。PowerPoint ファイルが座標、スキーマ参照、XML リレーションシップツリーで構成された単純な zip パッケージであることを認識すれば、既存のサードパーティ抽象に縛られることはなくなります。

スライド生成のスループットを最適化する場合でも、カスタム自動サニタイザーを作成する場合でも、レンダリングの不具合をトラブルシュートする場合でも、基盤となる OpenXML アーキテクチャを直接見ることで、プレゼンテーションパイプラインを完全に制御できます。

よくある質問 (FAQ)

**Q: .pptx を標準フォルダーに変換し、IDE で直接ファイルを編集できますか?

A1: はい、アーカイブを展開し、VS Code のようなエディタで XML を編集し、ディレクトリの内容を再度 zip 圧縮すれば、PowerPoint で再び開くことができます。

**Q: PowerPoint が手動で変更した PPTX ファイルの修復が必要だと言うのはなぜですか?

A2: これは通常、XML が不正な形式になっていたり、[Content_Types].xml から新しいアセットを省略したり、関連する .rels ファイルに未解決の参照が残っている場合に発生します。

**Q: PowerPoint は図形の位置や余白にどの測定単位を使用しますか?

A3: PowerPoint は英語メトリック単位 (EMUs) を使用し、1 インチは 914,400 EMU、1 ポイントは 12,700 EMU に相当します。

**Q: 外部ライブラリを使用せずにプレゼンテーションからすべての画像をプログラムで抽出するにはどうすればよいですか?

A4: 任意の標準的な zip ユーティリティで .pptx ファイルを開き、ppt/media/ ディレクトリ内にあるすべてのバイナリファイルを抽出するだけです。

**Q: 標準の XML パーサーでユーザーがアップロードした PPTX ファイルを解析するのは安全ですか?

A5: いいえ、パーサーを強化するか、defusedxml のような安全なラッパーを使用して XML 外部エンティティ (XXE) や zip‑bomb 攻撃を防止する必要があります。

関連項目