التقاط كود Claude باستخدام `mitmproxy` — دليل خطوة بخطوة (مع إضافات جاهزة للتشغيل & سكربتات تحليل)

٢٢ سبتمبر ٢٠٢٥

Capture Claude Code with `mitmproxy` — step-by-step guide (with ready-to-run addons & analysis scripts)

هل ترغب في رؤية ما يرسله Claude Code فعليًا إلى Anthropic؟ هذه المقالة تشرح لك سير عمل عملي وقابل للتكرار لاعتراض وحفظ وتحليل حركة مرور Claude Code باستخدام mitmproxy. ستحصل على:

  • إعداد خطوة بخطوة لبروكسي عكسي لـ Claude Code باستخدام mitmproxy.
  • إضافة جاهزة للتشغيل تستخرج الرسائل النظامية المخفية (القواعد «المهم: …»).
  • إضافة متكاملة تلتقط الطلبات + الاستجابات (الرسائل النظامية، تعريفات الأدوات، رسائل المستخدم، والاستجابات المتدفقة/الجزئية) في سجلات موقوتة.
  • أدوات تحليل صغيرة لاستخراج الرسائل النظامية وربط الطلبات بالاستجابات في تنسيق JSON لإعادة التشغيل/التحليل.
  • تحذيرات أمنية وخصوصية وقانونية.

ملخص سريع: شغّل mitmproxy في الوضع العكسي، اضبط ANTHROPIC_BASE_URL=http://localhost:8000, ثق بشهادة mitm أثناء الاختبار، شغّل Claude Code — وستحفظ الإضافات أدناه الحمولات لك.


السلامة والأخلاقيات (اقرأ أولاً)

  • لا تعترض إلا حركة مرور تملكها أو مُصرح لك صراحة بمراجعتها. اعتراض حركة مرور المستخدمين الآخرين غير قانوني/غير أخلاقي.
  • ستحتوي السجلات الملتقطة على API مفاتيح ورموز وأسرار. تعامل معها بحساسية. قم بحذف المعلومات الحساسة قبل المشاركة.
  • بعد الاختبار، أزل شهادة mitmproxy CA من مخزن الثقة الخاص بنظامك. لا تثق بها بشكل دائم.
  • إذا كان العميل يستخدم تثبيت TLS، فستتم مقاطعتك — لا تحاول تجاوز التثبيت لأنظمة لا تملكها.

المتطلبات الأساسية

  • macOS أو Linux (الأوامر مذكورة لـ macOS؛ Linux مشابه).
  • Python 3.8+ (لإضافة mitmproxy).
  • Homebrew (اختياري) أو pip لتثبيت mitmproxy.
  • عميل claude/Claude Code الذي يمكن توجيهه إلى ANTHROPIC_BASE_URL مخصص (أو أي عميل يتصل بـ API.anthropic.com).

1- تثبيت mitmproxy

macOS (Homebrew):

brew install mitmproxy

أو باستخدام pip (يفضل virtualenv):

python3 -m venv venv
source venv/bin/activate
pip install mitmproxy

الأدوات التي لديك الآن:

  • mitmproxy — واجهة طرفية تفاعلية
  • mitmweb — واجهة ويب (مُفتش الويب الافتراضي على :8081)
  • mitmdump — وضع بدون واجهة/سكربت (ممتاز لتشغيل الإضافات)

2— تشغيل mitmproxy في الوضع العكسي

يقوم Claude Code عادةً بالاتصال بـ https://API.anthropic.com. شغّل mitmproxy كـ بروكسي عكسي يوجه إلى Anthropic API الحقيقي:

mitmweb --mode reverse:https://API.anthropic.com --listen-port 8000
  • يستمع على http://localhost:8000 ويوجه إلى https://API.anthropic.com.
  • يمكنك الوصول إلى الواجهة عبر http://127.0.0.1:8081 لمراجعة التدفقات مباشرة.

3— الثقة بشهادة mitmproxy CA (لـ HTTPS)

تقوم mitmproxy بإنشاء CA محلي لتوقيع شهادات TLS للمضيفين المُعتَرض عليهم. لتجنب أخطاء TLS، يجب عليك الثقة بها مؤقتًا:

  1. شغّل mitmproxy مرة واحدة — سيقوم بإنشاء الشهادات في ~/.mitmproxy/mitmproxy-ca-cert.pem.
  2. على macOS: افتح Keychain AccessFile → Import Items → استورد ~/.mitmproxy/mitmproxy-ca-cert.pem.
    • انقر مرتين على الشهادة → Trust → اضبط Always Trust لـ SSL.
  3. لـ Firefox أو iOS/Android، استورد CA إلى مخازن الشهادات الخاصة بهم (Firefox يستخدم مخزن شهادات خاص به).

مهم: عند الانتهاء من الاختبار، أزل شهادة mitmproxy CA من مخازن الثقة.


4— توجيه Claude Code إلى mitmproxy

اضبط عنوان URL الأساسي لـ Anthropic إلى عنوان mitmproxy قبل تشغيل Claude Code:

export ANTHROPIC_BASE_URL="http://localhost:8000"
claude

الآن Claude Code → mitmproxy → Anthropic API.


5— ما ستلتقطه

الأمور النموذجية التي ستلتقطها في الطلبات:

  • حقل system: الرسالة النظامية المخفية / قواعد السياسة (النص «المهم: …»).
  • tools: مخطط الأداة (البحث، git، التحرير، إلخ.).
  • messages: رسائل المستخدم / المساعد.

قد تصل الاستجابات بشكل متدفق ومجزأ (قطع JSON جزئية). mitmproxy يعرض محتوى الطلبات والاستجابات بحيث يمكنك حفظها.


6— إضافة جاهزة للتشغيل: استخراج الرسائل النظامية فقط

أنشئ dump_claude_prompts.py بهذا المحتوى. يستخرج حقول system ويضيفها إلى ملف نصي.

# dump_claude_prompts.py
from mitmproxy import http
import json

OUTPUT_FILE = "claude_system_prompts.txt"

def request(flow: http.HTTPFlow):
    if "anthropic.com" not in flow.request.pretty_host:
        return
    try:
        body = flow.request.get_text()
        data = json.loads(body)
        if "system" in data:
            system_prompt = data["system"]
            with open(OUTPUT_FILE, "a", encoding="utf-8") as f:
                f.write("\n=== New System Prompt Captured ===\n")
                f.write(system_prompt)
                f.write("\n----------------------------------\n")
            print(f"[+] Captured system prompt ({len(system_prompt)} chars)")
    except Exception as e:
        print(f"[!] Error parsing request: {e}")

شغّله:

mitmdump -s dump_claude_prompts.py --mode reverse:https://API.anthropic.com --listen-port 8000

الإخراج: claude_system_prompts.txt يحتوي على كل كتلة system ملتقطة.


7— إضافة موسعة: التقاط موجّهات النظام + تعريفات الأدوات

إذا كنت تريد التقاط كل من موجّهات النظام وتعريفات tools في ملف نصي منظم، استخدم:

# dump_claude_prompts_and_tools.py
from mitmproxy import http
import json

OUTPUT_FILE = "claude_prompts_and_tools.txt"

def request(flow: http.HTTPFlow):
    if "anthropic.com" not in flow.request.pretty_host:
        return
    try:
        body = flow.request.get_text()
        data = json.loads(body)
        with open(OUTPUT_FILE, "a", encoding="utf-8") as f:
            f.write("\n==============================\n")
            f.write(" NEW CLAUDE REQUEST CAPTURED\n")
            f.write("==============================\n\n")
            if "system" in data:
                f.write("### SYSTEM PROMPT ###\n")
                f.write(data["system"].strip())
                f.write("\n\n")
            if "tools" in data:
                f.write("### TOOL DEFINITIONS ###\n")
                for tool in data["tools"]:
                    name = tool.get("name", "<no name>")
                    desc = tool.get("description", "<no description>")
                    params = json.dumps(tool.get("parameters", {}), indent=2)
                    f.write(f"- Tool: {name}\n")
                    f.write(f"  Description: {desc}\n")
                    f.write(f"  Parameters:\n{params}\n\n")
            f.write("----------------------------------\n")
        print("[+] Captured Claude system + tools")
    except Exception as e:
        print(f"[!] Error parsing request: {e}")

شغّل:

mitmdump -s dump_claude_prompts_and_tools.py --mode reverse:https://API.anthropic.com --listen-port 8000

مخرجات: claude_prompts_and_tools.txt


هذه الإضافة الكاملة تُنشئ ملفًا موقوتًا .log لكل زوج طلب/استجابة. احفظ باسم dump_claude_full.py.

# dump_claude_full.py
from mitmproxy import http
import json, os, time

OUTPUT_DIR = "claude_captures"
os.makedirs(OUTPUT_DIR, exist_ok=True)

def _safe_json_parse(text: str):
    try:
        return json.loads(text)
    except Exception:
        return None

def _write_section(f, title: str, content: str):
    f.write(f"\n### {title} ###\n")
    f.write(content.strip() if content else "<empty>")
    f.write("\n")

def request(flow: http.HTTPFlow):
    if "anthropic.com" not in flow.request.pretty_host:
        return
    ts = time.strftime("%Y%m%d-%H%M%S")
    filename = os.path.join(OUTPUT_DIR, f"claude_{ts}_{flow.id}.log")
    body = flow.request.get_text()
    data = _safe_json_parse(body)
    with open(filename, "w", encoding="utf-8") as f:
        f.write("=====================================\n")
        f.write(f" CLAUDE REQUEST  {time.ctime()}\n")
        f.write("=====================================\n")
        if data and "system" in data:
            _write_section(f, "SYSTEM PROMPT", data["system"])
        if data and "tools" in data:
            tool_dump = []
            for tool in data["tools"]:
                name = tool.get("name", "<no name>")
                desc = tool.get("description", "<no description>")
                params = json.dumps(tool.get("parameters", {}), indent=2)
                tool_dump.append(f"- {name}: {desc}\n  Params: {params}")
            _write_section(f, "TOOLS", "\n".join(tool_dump))
        if data and "messages" in data:
            messages = []
            for msg in data["messages"]:
                role = msg.get("role", "?")
                content = msg.get("content", "")
                messages.append(f"{role.upper()}: {content}")
            _write_section(f, "MESSAGES", "\n".join(messages))
        f.write("\n--- Waiting for response ---\n")
    flow.metadata["dump_file"] = filename
    print(f"[+] Captured request → {filename}")

def response(flow: http.HTTPFlow):
    if "anthropic.com" not in flow.request.pretty_host:
        return
    filename = flow.metadata.get("dump_file")
    if not filename:
        ts = time.strftime("%Y%m%d-%H%M%S")
        filename = os.path.join(OUTPUT_DIR, f"claude_{ts}_{flow.id}.log")
    text = flow.response.get_text()
    data = _safe_json_parse(text)
    with open(filename, "a", encoding="utf-8") as f:
        f.write("\n=====================================\n")
        f.write(f" CLAUDE RESPONSE  {time.ctime()}\n")
        f.write("=====================================\n")
        if data:
            formatted = json.dumps(data, indent=2, ensure_ascii=False)
            _write_section(f, "RAW JSON RESPONSE", formatted)
        else:
            _write_section(f, "RAW TEXT RESPONSE", text)
        f.write("\n========== END OF CAPTURE ==========\n")
    print(f"[+] Appended response → {filename}")

شغّل التقاط:

mitmdump -s dump_claude_full.py --mode reverse:https://API.anthropic.com --listen-port 8000

إخراج: ملف .log واحد لكل طلب في claude_captures/. كل ملف يحتوي على:

  • SYSTEM PROMPT
  • TOOLS
  • MESSAGES
  • RAW JSON RESPONSE أو RAW TEXT RESPONSE (قطع تدفق مضمنة)

9— محلل: استخراج و إزالة التكرار لمطالبات النظام

استخدم هذا النص البرمجي extract_system_prompts.py لفحص سجلات claude_captures/، استخراج أقسام مطالبات النظام، إزالة التكرار باستخدام SHA-256، و اكتب:

  • all_system_prompts.txt — مطالبات قابلة للقراءة البشرية (واحد لكل مطالبة فريدة)
  • system_prompts_index.csv — فهرس مع filename, prompt_excerpt, captured_at, و hash.

extract_system_prompts.py (احفظ و شغل):

#!/usr/bin/env python3
import os, re, hashlib, csv, argparse, datetime, json

SYSTEM_HEADING = "### SYSTEM PROMPT ###"
DIVIDER = "\n=== PROMPT ===\n"

def extract_system_from_text(text):
    pattern = re.compile(r"###\s*SYSTEM PROMPT\s*###\s*(.*?)\s*(?=###\s*\w+\s*###|$)", re.S|re.I)
    m = pattern.search(text)
    if m:
        return m.group(1).strip()
    return None

def scan_directory(dirname):
    prompts = []
    for root, _, files in os.walk(dirname):
        for fname in sorted(files):
            if not fname.lower().endswith(".log"):
                continue
            path = os.path.join(root, fname)
            with open(path, "r", encoding="utf-8") as fh:
                txt = fh.read()
            prompt = extract_system_from_text(txt)
            if prompt:
                h = hashlib.sha256(prompt.encode("utf-8")).hexdigest()
                captured_at = ""
                m = re.search(r"claude_(\d{8}-\d{6})", fname)
                if m:
                    try:
                        captured_at = datetime.datetime.strptime(m.group(1), "%Y%m%d-%H%M%S").isoformat()
                    except:
                        pass
                prompts.append({"filename": path, "prompt": prompt, "hash": h, "captured_at": captured_at})
    return prompts

def save_prompts(prompts, out_txt="all_system_prompts.txt", index_csv="system_prompts_index.csv"):
    seen = {}
    for p in prompts:
        h = p["hash"]
        if h not in seen or (p["captured_at"] and p["captured_at"] < seen[h]["captured_at"]):
            seen[h] = p
    unique = list(seen.values())
    unique.sort(key=lambda x: x["captured_at"] or "")
    with open(out_txt, "w", encoding="utf-8") as fh:
        for u in unique:
            fh.write(DIVIDER)
            fh.write(f"Source file: {u['filename']}\nCaptured at: {u['captured_at']}\n\n")
            fh.write(u["prompt"].strip())
            fh.write("\n")
    with open(index_csv, "w", newline='', encoding="utf-8") as csvf:
        writer = csv.writer(csvf)
        writer.writerow(["filename", "captured_at", "prompt_excerpt", "hash"])
        for u in unique:
            excerpt = u["prompt"].replace("\n", " ")[:200]
            writer.writerow([u["filename"], u["captured_at"], excerpt, u["hash"]])

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--dir", default="claude_captures")
    parser.add_argument("--out", default="all_system_prompts.txt")
    parser.add_argument("--index", default="system_prompts_index.csv")
    args = parser.parse_args()
    prompts = scan_directory(args.dir)
    if not prompts:
        print("No system prompts found.")
    else:
        save_prompts(prompts, out_txt=args.out, index_csv=args.index)
        print(f"Saved {len(prompts)} prompt entries to {args.out} and {args.index}")

شغل:

python3 extract_system_prompts.py --dir claude_captures --out all_system_prompts.txt --index system_prompts_index.csv

10— ربط الطلب + الاستجابة في JSON (لإعادة التشغيل/التحليل)

إذا كنت تفضل ربطًا صديقًا للآلة (JSON واحد لكل التقاط + ملف JSON رئيسي)، استخدم سكريبت الربط أدناه. يقرأ ملفات .log المُنتَجة بواسطة dump_claude_full.py، ويحلل الأقسام ويكتب:

  • paired_captures.json — مصفوفة رئيسية لكائنات التقاط
  • paired_jsons/*.json — ملفات JSON لكل التقاط

سكريبت الربط (تنفيذ مثال):

# pair_captures.py (concept)
# Pseudo: read .log files from claude_captures, parse headings using regex,
# construct object: {source_file, captured_at, system_prompt, tools (list), messages, raw_response, response_json}
# write per-capture JSONs into paired_jsons/, and a master paired_captures.json.

# (see the earlier detailed pairing code in this guide — you can copy it verbatim or adapt)

يمكنك بعد ذلك:

  • استخدم ملفات JSON لكل التقاط لإنشاء طلبات إعادة التشغيل (curl, httpie) أو لإدخالها في منصة اختبار محلية.
  • حول التقاطات إلى تنسيق mitmproxy server-replay إذا كنت تريد إعادة تشغيل حركة المرور بدقة دون اتصال.

11— إعادة تشغيل التقاطات (ملاحظة سريعة)

  • يدعم mitmproxy server-replay باستخدام التدفقات المسجلة (mitmdump -w flows.mitm للتسجيل؛ --server-replay flows.mitm لإعادة التشغيل).
  • يُقصد من إخراج ربط JSON للتحليل البرمجي أو سكريبتات إعادة التشغيل المخصصة (curl/requests). لإجراء إعادة تشغيل طلب/استجابة حقيقية بتنسيق mitmproxy، فكر في تسجيل ملفات تدفق .mitm باستخدام mitmdump -w flows.mitm بدلاً من النصوص العادية .log.

12— حذف الأسرار والتنظيف

قبل مشاركة أو أرشفة السجلات:

  • قم بإزالة أو تخفيف قيم الرؤوس (Authorization, Cookies). مثال (مقتطف بايثون):
# naive header redaction example
if "Authorization" in flow.request.headers:
    flow.request.headers["Authorization"] = "<REDACTED>"
  • قم بإزالة ثقة CA من Keychain بعد الاختبار لاستعادة أمان النظام.
  • احتفظ بملفات التقاط في دليل مشفر إذا كانت ستبقى.

13— استكشاف الأخطاء وإصلاحها ونصائح

  • أخطاء TLS → ربما لم تثق في CA الخاص بـ mitm أو العميل يربط الشهادات.
  • لا يوجد حركة مرور مُلتقطة → تأكد من ضبط ANTHROPIC_BASE_URL على http://localhost:8000 أو تكوين العميل لاستخدام بروكسي HTTP.
  • استجابات JSON مجزأة → Anthropic غالبًا ما يبث JSON جزئي؛ يجب على محركات التحليل التعامل مع JSON جزئي/غير صالح بسلاسة (حاول التقاط RAW TEXT RESPONSE ثم إعادة البناء).
  • شهادة في الأجهزة المحمولة → استورد CA الخاص بـ mitm إلى مخزن الثقة للجهاز؛ بالنسبة لـ iOS، قد تواجه تعقيدات في التخصيص.
  • سجلات كبيرة → قم بتدوير السجلات أو ضغطها. فكر في تخزين الحقول المستخرجة فقط (النظام، الأدوات، الرسائل) لتقليل الحجم.

14— مثال لسير العمل (تشغيل كامل)

  1. ابدأ mitmproxy مع الإضافة الكاملة:
mitmdump -s dump_claude_full.py --mode reverse:https://API.anthropic.com --listen-port 8000
  1. صدر البيئة وقم بتشغيل Claude Code:
export ANTHROPIC_BASE_URL="http://localhost:8000"
claude
# interact with Claude Code like normal
  1. بعد الجلسة، أوقف mitmproxy. راجع claude_captures/ للبحث عن ملفات .log ذات الطوابع الزمنية.
  2. استخرج محفزات النظام الفريدة:
python3 extract_system_prompts.py --dir claude_captures --out all_system_prompts.txt --index system_prompts_index.csv
  1. ربط الطلب والاستجابة في JSON:
python3 pair_captures.py   # or run the ready pairing script provided above
  1. قم بتخفيف الأسرار وإزالة CA الخاص بـ mitm من الثقة (تنظيف).

ملاحظات ختامية

هذا سير العمل هو الطريقة التي استخدمها الباحثون والمهندسين لفحص عملاء مثل Claude Code لفهم:

  • كيف يتم توصيل محفزات النظام/القيود إلى النموذج، وكيف يتم التعامل مع JSON الجزئي المتدفق، وما هي السياسات التي يضيفها العميل إلى كل طلب.