Communitygithub.com

Ghidra で Golang マルウェアを解析 (analyzing-golang-malware-with-ghidra)

Ghidra による Go コンパイルマルウェアのリバースエンジニアリング。Go ビルド情報を解析し、パッケージ名や関数名を復元します。

Ghidra で Golang マルウェアを解析 (analyzing-golang-malware-with-ghidra) とは?

このスキルは、Ghidra を用いて Go 言語でコンパイルされたマルウェアを解析するための包括的な手順とスクリプトを提供します。Go バイナリはランタイムと全依存関係を静的にリンクするため、巨大で解析が困難ですが、本スキルでは pclntab(プログラムカウンタ行テーブル)や Go 固有のメタデータを活用して、難読化・ストリップされたバイナリから関数名やサードパーティ依存関係を復元します。 主な機能として、Go ビルド情報の抽出、pclntab マジックバイトによる Go バージョン特定、pclntab からの関数名復元、依存関係(モジュールパス)の抽出、Go 文字列(ポインタ+長さペア)の抽出を行います。また、Ghidra 内で実行可能な Python スクリプトも提供し、pclntab のラベル作成や関数シンボルの自動検出を支援します。 このスキルは、Ghidra 11.0+(JDK 17+)、GoResolver プラグイン(Volexity 2025)、go-re.tk ツールキット、Python 3.9+ を前提としています。Go ランタイム内部構造(goroutine、channel、interface)とバイナリ構造(pclntab、moduledata、itab)の理解が推奨されます。 特に、SOC アナリストや脅威ハンターがセキュリティインシデント調査や検知ルール作成を行う際に有用です。Go マルウェアの静的解析を効率化し、マルウェアの能力評価を迅速化します。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/mukul975/Anthropic-Cybersecurity-Skills/tree/main/skills/analyzing-golang-malware-with-ghidra

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Ghidra で Golang マルウェアを解析

概要

Go (Golang) は、クロスコンパイル機能、自己完結型バイナリを生成する静的リンク、そしてリバースエンジニアリングに複雑さをもたらす特性から、マルウェア作者に人気の言語になっています。Go バイナリはランタイム全体、標準ライブラリ、すべての依存関係を静的にリンクするため、結果として非常に大きなバイナリ(多くの場合 5~15MB)となり、数千もの関数が含まれます。Ghidra は、Go 固有の文字列形式(NULL 終端ではない)、ストリップされた関数名、goroutine の並行処理パターンに対応するのに苦労します。GoResolver(Volexity、2025)のような専門ツールは、制御フローグラフの類似性を利用して、ストリップまたは難読化された Go バイナリの関数名を自動的に復元・復号します。

使用すべき状況

  • Ghidra で Golang マルウェアを解析する必要があるセキュリティインシデントを調査する場合
  • この分野に関する検知ルールや脅威ハンティングクエリを作成する場合
  • SOC アナリストがこの分析タイプの構造化された手順を必要とする場合
  • 関連する攻撃手法に対するセキュリティ監視範囲を検証する場合

前提条件

  • Ghidra 11.0+(JDK 17+)
  • GoResolver プラグイン(関数名復元用)
  • Go Reverse Engineering Tool Kit (go-re.tk)
  • ヘルパースクリプト用 Python 3.9+
  • Go ランタイム内部構造(goroutine、channel、interface)の理解
  • Go バイナリ構造(pclntab、moduledata、itab)への精通

主要な概念

Go バイナリ構造

Go バイナリは、プログラムカウンタを関数名、ソースファイル、行番号にマッピングする pclntab(プログラムカウンタ行テーブル)構造体に豊富なメタデータを埋め込みます。ストリップされたバイナリでもこのメタデータは保持されます。moduledata 構造体には、型情報、itab(インターフェーステーブル)、pclntab 自体へのポインタが含まれます。Go 文字列は、NULL 終端の C 文字列ではなく、ポインタと長さのペアとして格納されます。

ストリップされたバイナリにおける関数復元

シンボルテーブルがストリップされていても、Go バイナリは pclntab 内に関数名を保持しています。ただし、garble のような難読化ツールは、関数名をランダムな文字列に変更します。GoResolver は、難読化された関数の制御フローグラフシグネチャを計算し、既知の Go 標準ライブラリおよびサードパーティパッケージ関数のデータベースと照合することで、この問題に対処します。

クレート/依存関係の抽出

Go の依存関係管理は、モジュールパスとバージョン文字列をバイナリに埋め込みます。これらを抽出することで、マルウェアのサードパーティ依存関係(HTTP ライブラリ、暗号化パッケージ、C2 フレームワーク)が明らかになり、完全なリバースエンジニアリングを必要とせずに機能に関する洞察が得られます。

ワークフロー

ステップ 1: 初期バイナリ分析

#!/usr/bin/env python3
"""マルウェア解析のための Go バイナリメタデータ解析。"""
import struct
import sys
import re


def find_go_build_info(data):
    """バイナリから Go ビルド情報を抽出する。"""
    # Go buildinfo マジック: \xff Go buildinf:
    magic = b'\xff Go buildinf:'
    offset = data.find(magic)
    if offset == -1:
        return None

    print(f"[+] Go ビルド情報がオフセット 0x{offset:x} にあります")

    # 近くの Go バージョン文字列を抽出
    go_version = re.search(rb'go\d+\.\d+(?:\.\d+)?', data[offset:offset+256])
    if go_version:
        print(f"  Go バージョン: {go_version.group().decode()}")

    return offset


def find_pclntab(data):
    """pclntab(プログラムカウンタ行テーブル)構造体を特定する。"""
    # pclntab マジックバイトは Go バージョンによって異なる
    magics = {
        b'\xfb\xff\xff\xff\x00\x00': "Go 1.2-1.15",
        b'\xfa\xff\xff\xff\x00\x00': "Go 1.16-1.17",
        b'\xf1\xff\xff\xff\x00\x00': "Go 1.18-1.19",
        b'\xf0\xff\xff\xff\x00\x00': "Go 1.20+",
    }

    for magic, version in magics.items():
        offset = data.find(magic)
        if offset != -1:
            print(f"[+] pclntab が 0x{offset:x} にあります ({version})")
            return offset, version

    return None, None


def extract_function_names(data, pclntab_offset):
    """pclntab から関数名を抽出する。"""
    if pclntab_offset is None:
        return []

    functions = []
    # 関数名文字列は特定のパターンに従う
    func_pattern = re.compile(
        rb'(?:main|runtime|fmt|net|os|crypto|encoding|io|sync|'
        rb'syscall|reflect|strings|bytes|path|time|math|sort|'
        rb'github\.com|golang\.org)[/\.][\w/.]+',
    )

    for match in func_pattern.finditer(data):
        name = match.group().decode('utf-8', errors='replace')
        if len(name) > 4 and len(name) < 200:
            functions.append(name)

    return sorted(set(functions))


def extract_go_strings(data):
    """Go 形式の文字列(ポインタ+長さペア)を抽出する。"""
    # Go 文字列は NULL 終端ではない。読み取り可能なシーケンスを抽出する
    strings = []
    ascii_pattern = re.compile(rb'[\x20-\x7e]{10,}')

    for match in ascii_pattern.finditer(data):
        s = match.group().decode('ascii')
        # 興味深いマルウェア文字列にフィルタリング
        interesting = [
            'http', 'https', 'tcp', 'udp', 'dns',
            'cmd', 'shell', 'exec', 'upload', 'download',
            'encrypt', 'decrypt', 'key', 'token', 'password',
            'c2', 'beacon', 'agent', 'implant', 'bot',
            'mutex', 'persist', 'registry', 'scheduled',
        ]
        if any(kw in s.lower() for kw in interesting):
            strings.append(s)

    return strings


def extract_dependencies(data):
    """バイナリから Go モジュールの依存関係を抽出する。"""
    deps = []
    # モジュールパスは github.com/user/repo のパターンに従う
    dep_pattern = re.compile(
        rb'((?:github\.com|gitlab\.com|golang\.org|gopkg\.in|'
        rb'go\.etcd\.io|google\.golang\.org)/[^\x00\s]{5,80})'
    )

    for match in dep_pattern.finditer(data):
        dep = match.group().decode('utf-8', errors='replace')
        deps.append(dep)

    unique_deps = sorted(set(deps))
    return unique_deps


def analyze_go_binary(filepath):
    """Go マルウェアバイナリの完全解析。"""
    with open(filepath, 'rb') as f:
        data = f.read()

    print(f"[+] Go バイナリを解析中: {filepath}")
    print(f"  ファイルサイズ: {len(data):,} バイト")
    print("=" * 60)

    # ビルド情報
    find_go_build_info(data)

    # pclntab
    pclntab_offset, go_version = find_pclntab(data)

    # 関数
    functions = extract_function_names(data, pclntab_offset)
    print(f"\n[+] {len(functions)} 個の関数名を復元しました")

    # 関数をカテゴリ分け
    categories = {
        "network": [], "crypto": [], "os_exec": [],
        "file_io": [], "main": [], "third_party": [],
    }
    for f in functions:
        if 'net/' in f or 'http' in f.lower():
            categories["network"].append(f)
        elif 'crypto' in f:
            categories["crypto"].append(f)
        elif 'os/exec' in f or 'syscall' in f:
            categories["os_exec"].append(f)
        elif 'os.' in f or 'io/' in f:
            categories["file_io"].append(f)
        elif f.startswith('main.'):
            categories["main"].append(f)
        elif 'github.com' in f or 'golang.org' in f:
            categories["third_party"].append(f)

    for cat, funcs in categories.items():
        if funcs:
            print(f"\n  [{cat}] ({len(funcs)} 関数):")
            for fn in funcs[:10]:
                print(f"    {fn}")

    # 依存関係
    deps = extract_dependencies(data)
    print(f"\n[+] 依存関係 ({len(deps)}):")
    for dep in deps[:20]:
        print(f"    {dep}")

    # 不審な文字列
    sus_strings = extract_go_strings(data)
    print(f"\n[+] 不審な文字列 ({len(sus_strings)}):")
    for s in sus_strings[:20]:
        print(f"    {s}")


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"使い方: {sys.argv[0]} <go_binary>")
        sys.exit(1)
    analyze_go_binary(sys.argv[1])

ステップ 2: Ghidra 解析スクリプト

# Ghidra スクリプト(Ghidra のスクリプトマネージャ内で実行)
# AnalyzeGoBinary.py として Ghidra スクリプトディレクトリに保存

# @category MalwareAnalysis
# @description Go バイナリ構造を解析しメタデータを復元する

def analyze_go_binary_ghidra():
    """Go バイナリ解析用 Ghidra スクリプト。"""
    from ghidra.program.model.mem import MemoryAccessException

    program = getCurrentProgram()
    memory = program.getMemory()
    listing = program.getListing()

    print("[+] Go バイナリ解析スクリプト")
    print(f"  プログラム: {program.getName()}")

    # pclntab を検索
    pclntab_magics = [
        bytes([0xf0, 0xff, 0xff, 0xff]),  # Go 1.20+
        bytes([0xf1, 0xff, 0xff, 0xff]),  # Go 1.18-1.19
        bytes([0xfa, 0xff, 0xff, 0xff]),  # Go 1.16-1.17
        bytes([0xfb, 0xff, 0xff, 0xff]),  # Go 1.2-1.15
    ]

    for magic in pclntab_magics:
        addr = memory.findBytes(
            program.getMinAddress(), magic, None, True, None
        )
        if addr:
            print(f"[+] pclntab が {addr} にあります")
            # ラベルを作成
            program.getSymbolTable().createLabel(
                addr, "go_pclntab", None,
                ghidra.program.model.symbol.SourceType.ANALYSIS
            )
            break

    # Go 文字列定義の修正
    # Go 文字列は ptr+len であり、NULL 終端ではない
    print("[+] Go 文字列参照を修正中...")

    # パッケージパスを含む関数名を検索
    symbol_table = program.getSymbolTable()
    func_count = 0
    for symbol in symbol_table.getAllSymbols(True):
        name = symbol.getName()
        if ('.' in name and
            any(pkg in name for pkg in
                ['main.', 'runtime.', 'net.', 'crypto.', 'os.'])):
            func_count += 1

    print(f"[+] {func_count} 個の Go 関数シンボルを検出しました")


# 実行
analyze_go_binary_ghidra()

検証基準

  • Go バージョンとビルド情報がバイナリから抽出されていること
  • pclntab が特定され、関数名復元のために解析されていること
  • サードパーティの依存関係が特定され、マルウェアの機能が明らかになっていること
  • main パッケージの関数が列挙され、対象を絞った分析が可能になっていること
  • ネットワーク、暗号化、OS 実行関数がカテゴリ分けされていること
  • Ghidra 解析が Go ランタイム構造を正しくラベル付けしていること

参考資料

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

abusing-dpapi-for-credential-access

Extract and decrypt Windows DPAPI-protected secrets (Credential Manager, browser logins/cookies, Wi-Fi credentials, KeePass keys) online or offline using SharpDPAPI, SharpChrome, Mimikatz, or Impacket

abusing-shadow-credentials-for-privesc

Take over Active Directory accounts by writing attacker-controlled public keys to msDS-KeyCredentialLink (Shadow Credentials) with pyWhisker, Whisker, or Certipy, then authenticate via PKINIT to recover the target

achieving-cmmc-level-2-compliance

>-

acquiring-disk-image-with-dd-and-dcfldd

Create forensically sound bit-for-bit disk images with dd or dcfldd on a Linux forensic workstation, preserving evidence integrity through hash verification (MD5/SHA) during acquisition. Use when imaging a suspect drive, USB device, or memory card for investigation, preserving volatile disk evidence during incident response, or producing a verified copy for legal or law-enforcement proceedings before any destructive analysis.

analyzing-active-directory-acl-abuse

Detect dangerous ACL misconfigurations in Active Directory using ldap3

analyzing-android-malware-with-apktool

Perform static analysis of Android APK malware using apktool for resource decompilation, jadx for Java source recovery, and androguard for manifest inspection, dangerous permission-combination detection, and identification of obfuscated code, dynamic code loading, and reflection-based API calls. Use to statically triage a suspicious APK without executing it or to build mobile malware detection rules.

analyzing-api-gateway-access-logs

Parses API Gateway access logs (AWS API Gateway, Kong, Nginx) to detect

analyzing-apt-group-with-mitre-navigator

Query ATT&CK data with attackcti, mitreattack-python, and stix2, then build MITRE ATT&CK Navigator layers and multi-layer heatmap overlays mapping one or more APT groups

analyzing-azure-activity-logs-for-threats

Queries Azure Monitor activity logs and sign-in logs via azure-monitor-query

analyzing-bootkit-and-rootkit-samples

Analyzes bootkit and advanced rootkit malware infecting the Master

analyzing-browser-forensics-with-hindsight

Parse Chromium-based browser databases with Hindsight to extract and correlate browsing history, downloads, cookies, cached content, autofill data, saved passwords, and extensions from Chrome, Edge, Brave, Opera, and Vivaldi into a unified timeline (XLSX, JSON, or SQLite output). Use during incident response, insider-threat investigations, or criminal cases when you need to reconstruct a user

analyzing-campaign-attribution-evidence

Systematically evaluate cyber-campaign evidence to attribute an operation to a threat actor, using the Diamond Model and Analysis of Competing Hypotheses (ACH) to weigh infrastructure overlaps, TTP consistency, malware code similarity, and timing/language artifacts into confidence-weighted attribution assessments. Use when an incident investigation needs a defensible attribution confidence level.

analyzing-certificate-transparency-for-phishing

Monitor Certificate Transparency logs using crt.sh and Certstream to

analyzing-cloud-storage-access-patterns

Detect abnormal access in AWS S3, GCS, and Azure Blob Storage by analyzing CloudTrail Data Events, GCS audit logs, and Azure Storage Analytics for after-hours bulk downloads, new-IP access, and API-call spikes (e.g. GetObject) via statistical baselines and time-series anomaly detection. Use when investigating suspected cloud data exfiltration or building related detection rules.

analyzing-cobalt-strike-beacon-configuration

Extract and analyze Cobalt Strike beacon configuration from PE files

analyzing-cobaltstrike-malleable-c2-profiles

Parse and analyze Cobalt Strike Malleable C2 profiles with dissect.cobaltstrike (profiles and beacon-payload configs) and pyMalleableC2 (AST parsing) to extract HTTP/DNS transforms, URIs, headers, sleep/jitter, and injection behavior, then generate network detection signatures. Use when reverse-engineering a captured malleable profile or building detections against Cobalt Strike Beacon traffic.

analyzing-command-and-control-communication

Analyzes malware C2 communication over HTTP, HTTPS, DNS, and custom

analyzing-cyber-kill-chain

Analyzes intrusion activity against the Lockheed Martin Cyber Kill Chain

analyzing-disk-image-with-autopsy

Perform comprehensive forensic analysis of raw (dd), E01, or AFF disk images with Autopsy and The Sleuth Kit, recovering deleted files, examining metadata and embedded artifacts, keyword searching, and building investigation timelines with visual reports. Use for structured analysis of a forensic disk image or when stakeholders need visual reports from evidence.

analyzing-dns-logs-for-exfiltration

Analyzes DNS query logs to detect data exfiltration via DNS tunneling,

関連スキル