"""d01-d08: Fundamentals."""
from diag import *


def d01():
    fig, ax = canvas("COM activation: from CoCreateInstance to a live object",
                     w=13.0, h=7.6,
                     caption="in-proc: same process, no boundary crossed   ·   out-of-proc: RPC channel + proxy/stub pair")
    actors = [("Client process", 13, 18), ("combase / ole32", 38, 20),
              ("Registry", 61, 16), ("SCM / RPCSS", 86, 18),
              ("Server process", 113, 20)]
    top = 66.0
    for name, x, w in actors:
        box(ax, x, top, w, 7, name, fs=9.5, weight="bold")
        seq_lifeline(ax, x, top - 3.5, 8.0)
    X = {"cli": 13, "com": 38, "reg": 61, "scm": 86, "srv": 113}

    def msg(y, a, b, n, txt, mono=True, fs=8.5, ly=1.4):
        arrow(ax, (X[a], y), (X[b], y), kind="normal")
        label(ax, (X[a] + X[b]) / 2, y + ly, txt, fs=fs, mono=mono, va="bottom")
        if n:
            step_circle(ax, X[a] + (2.8 if X[b] > X[a] else -2.8), y, n)

    msg(58.0, "cli", "com", 1, "CoCreateInstance(CLSID, IID)")
    msg(52.5, "com", "reg", 2, "lookup HKCR\\CLSID\\{...}")
    msg(47.0, "reg", "com", 3, "3a  InprocServer32 (in-proc DLL)")
    arrow(ax, (X["com"], 42.5), (X["cli"], 42.5), kind="normal")
    label(ax, 25.5, 43.9, "LoadLibrary in client\n(no proxy)", fs=8.5, mono=True, va="bottom")
    msg(37.5, "reg", "com", 3, "3b  LocalServer32 (out-of-proc)")
    msg(33.0, "com", "scm", None, "RPC activation request")
    msg(28.0, "scm", "srv", 4, "start service / exe")
    msg(23.5, "srv", "scm", 5, "CoRegisterClassObject")
    msg(18.5, "scm", "com", 6, "IClassFactory::CreateInstance + proxy/stub")
    arrow(ax, (X["com"], 14.0), (X["cli"], 14.0), kind="normal")
    label(ax, 25.5, 15.4, "client holds\ninterface pointer", fs=8.5, va="bottom")
    return save(fig, "d01-com-activation-flow.png")


def d02():
    fig, ax = canvas("In-proc vs out-of-proc: where the trust boundary sits",
                     w=11.0, h=6.0)
    label(ax, 28.5, 52.5, "IN-PROC (DLL)", fs=10, weight="bold")
    label(ax, 82.0, 52.5, "OUT-OF-PROC (EXE / service)", fs=10, weight="bold")
    # left panel: single process boundary
    boundary(ax, 5, 12, 47, 34, "PROCESS — client token · medium IL")
    c = box(ax, 28.5, 39, 20, 8, "client code", fs=9.5)
    d = box(ax, 28.5, 24, 20, 8, "COM DLL", fs=9.5)
    arrow(ax, c.side("b"), d.side("t"), kind="normal",
          label="direct call\n(function pointer)", lx=13.5, fs=8.5)
    label(ax, 28.5, 15.5, "same token, same IL — no privilege boundary", fs=9)
    # right panel: two process boundaries
    boundary(ax, 52, 14, 23, 32, "CLIENT — medium IL")
    cb = box(ax, 63.5, 29, 16, 8, "client\nprocess", fs=9.5)
    boundary(ax, 87, 14, 22, 32, "SERVER —\nNT AUTHORITY\\SYSTEM")
    sb = box(ax, 98, 29, 16, 8, "OOP\nserver", fs=9.5)
    arrow(ax, cb.side("r"), sb.side("l"), kind="normal",
          label="RPC/ALPC\nNDR-marshaled\ncall", ly=2.0, fs=8.5, va="center")
    ax.plot([81, 81], [14, 22.5], color=SUB, lw=1.0, linestyle=BDASH, zorder=1)
    ax.plot([81, 81], [38.5, 46], color=SUB, lw=1.0, linestyle=BDASH, zorder=1)
    label(ax, 81, 47.5, "TRUST BOUNDARY", fs=8.5, color=SUB)
    label(ax, 81, 9.5, "crossing a privilege boundary =\nthe attack surface for LPE",
          fs=9, color=ATTACK, weight="bold")
    return save(fig, "d02-inproc-vs-oop.png")


def d03():
    fig, ax = canvas("HKCR is a merged view — and HKCU wins", w=11.0, h=6.0)
    hkcu = mbox(ax, 24, 47, "HKCU\\Software\\Classes", mono=True, fs=9.5,
                padx=3.0, pady=2.4)
    hklm = mbox(ax, 24, 33, "HKLM\\Software\\Classes", mono=True, fs=9.5,
                padx=3.0, pady=2.4)
    hkcr = box(ax, 79, 40, 40, 10, "HKCR view\nconsumed by activation", fs=9.5)
    elbow(ax, [hkcu.side("r"), (52, 47), (52, 43), hkcr.side("l", 0.75)],
          kind="normal")
    elbow(ax, [hklm.side("r"), (52, 33), (52, 37), hkcr.side("l", 0.25)],
          kind="normal")
    label(ax, 50, 50.5, "HKCU wins", fs=8.5, color=ATTACK, weight="bold")
    label(ax, 51, 29.5, "merge — on conflict HKCU shadows HKLM", fs=8.5, color=SUB)
    atk = box(ax, 79, 12, 38, 8, "Attacker — medium IL, no admin", fs=9.5)
    elbow(ax, [atk.side("t", 0.25), (69.5, 24), (6, 24), (6, 47), hkcu.side("l")],
          kind="attack",
          label="write HKCU\\Software\\Classes\\CLSID\\{target}\\InprocServer32",
          li=1, t=0.62, ly=1.4, fs=8.5, mono=True, va="bottom")
    label(ax, 40, 21.3, "per-user hijack: no admin needed", fs=9, color=ATTACK,
          weight="bold")
    return save(fig, "d03-hkcr-merge.png")


def d04():
    fig, ax = canvas("Marshaling: how a method call crosses processes",
                     w=13.0, h=5.6)
    cp = box(ax, 18, 42, 22, 9, "Client: proxy", fs=9.5)
    ch = box(ax, 56, 42, 28, 9, "COM channel / RPC runtime", fs=9.5)
    st = box(ax, 94, 42, 22, 9, "Server: stub", fs=9.5)
    conn(ax, cp, "r", ch, "l", label="NDR-marshaled call (IPID)", ly=4.7,
         fs=8.5, mono=True, va="bottom")
    conn(ax, ch, "r", st, "l", label="dispatch + unmarshal", ly=4.7, fs=8.5,
         va="bottom")
    # OBJREF layout box (under)
    ob = box(ax, 38, 20, 64, 16)
    label(ax, 38, 24.5, "OBJREF layout", fs=9.5, weight="bold")
    label(ax, 38, 21.0,
          "OXID (apartment/exporter) · OID (object) · IPID (interface)",
          fs=8.5, mono=True)
    label(ax, 38, 17.2,
          "resolution: IPID → interface on the object · OXID → IObjectExporter",
          fs=8.5, mono=True)
    label(ax, 38, 14.2, "the proxy holds the OBJREF; the stub holds the real object",
          fs=8.5, color=SUB)
    # side table (right)
    label(ax, 100, 31.5, "marshaling flavors", fs=9.5, weight="bold")
    mbox(ax, 100, 26.0, "standard   = MIDL/NDR proxy-stub DLL", mono=True,
         fs=8.5, padx=2.6)
    mbox(ax, 100, 20.0, "automation = type library (oleaut32)", mono=True,
         fs=8.5, padx=2.6)
    mbox(ax, 100, 13.0, "custom     = IMarshal — audit this", mono=True,
         fs=8.5, padx=2.6, edge=ATTACK, tc=ATTACK)
    label(ax, 100, 8.2, "hand-rolled deserialization — audit this", fs=8.5,
          color=ATTACK)
    return save(fig, "d04-marshaling-objref.png")


def d05():
    fig, ax = canvas("DCOM on the wire", w=13.0, h=5.2)
    boundary(ax, 4, 12, 30, 28, "HOST A")
    cli = box(ax, 19, 26, 20, 9, "client", fs=9.5)
    boundary(ax, 58, 8, 68, 37, "HOST B")
    rpc = box(ax, 72, 26, 18, 8, "RPCSS", fs=9.5, weight="bold")
    oxi = box(ax, 96, 26, 20, 8, "OXID resolver", fs=9.5)
    srv = box(ax, 117, 26, 16, 8, "Server object", fs=8.5)
    arrow(ax, cli.side("r"), rpc.side("l"), kind="normal",
          label="TCP 135\nIRemoteSCMActivator::\nRemoteCreateInstance",
          ly=1.6, fs=8.5, mono=True, va="bottom")
    step_circle(ax, 32.5, 26, 1)
    arrow(ax, rpc.side("r"), oxi.side("l"), kind="normal",
          label="IObjectExporter::\nResolveOxid2", ly=5.6, fs=8.5, mono=True,
          va="bottom")
    step_circle(ax, 83.5, 26, 2)
    elbow(ax, [(19, 21.5), (19, 5.5), (117, 5.5), (117, 22)], kind="normal",
          label="method calls — IRemoteUnknown::RemQueryInterface etc. · dynamic high ports",
          li=1, ly=1.3, fs=8.5, mono=True, va="bottom")
    step_circle(ax, 21.5, 5.5, 3)
    n = note(ax, 106, 39.8, "RoguePotato hijacks step 2\nwith a fake OXID resolver",
             kind="research", fs=8.5)
    ax.plot([97, 86.5], [35.6, 28.5], color=RES, lw=1.0, linestyle=DASH,
            zorder=2)
    return save(fig, "d05-dcom-wire.png")


def d06():
    fig, ax = canvas("The impersonation contract", w=12.0, h=6.5)
    cli = box(ax, 14, 44, 18, 9, "Client", fs=10, weight="bold")
    srv = box(ax, 58, 44, 18, 9, "Server", fs=10, weight="bold")
    arrow(ax, cli.side("r"), srv.side("l"), kind="normal",
          label="client sets\nCoSetProxyBlanket(...,\nRPC_C_IMP_LEVEL_*)  ·  call",
          ly=2.0, fs=8.5, mono=True, va="bottom")
    dia = diamond(ax, 58, 26, 34, 16, "impersonation level\n>= IMPERSONATE ?",
                  fs=9)
    arrow(ax, srv.side("b"), dia.side("t"), kind="normal",
          label="CoQueryClientBlanket()", lx=20.5, fs=8.5, mono=True)
    ok = box(ax, 97, 26, 32, 10, "CoImpersonateClient\nact with CLIENT token",
             fs=9)
    arrow(ax, dia.side("r"), ok.side("l"), kind="normal", label="yes",
          ly=1.3, va="bottom")
    label(ax, 97, 19.2, "safe: access checks see the caller", fs=8.5, color=SUB)
    bad = box(ax, 58, 8, 52, 9,
              "server acts with its own SYSTEM token\nbug class #1: missing/late impersonation",
              fs=9, edge=ATTACK, lw=2.2, tc=ATTACK)
    arrow(ax, dia.side("b"), bad.side("t"), kind="attack",
          label="no / missing check", lx=15.5, fs=8.5, lc=ATTACK)
    return save(fig, "d06-impersonation-contract.png")


def d07():
    fig, ax = canvas("Elevation moniker & auto-elevation", w=12.5, h=6.0)
    med = box(ax, 16, 42, 22, 9, "Medium-IL process", fs=9.5)
    app = box(ax, 56, 42, 22, 9, "APPINFO service", fs=9.5, weight="bold")
    arrow(ax, med.side("r"), app.side("l"), kind="normal",
          label="CoGetObject(\n\"Elevation:Administrator!new:{CLSID}\")",
          ly=4.8, fs=8.5, mono=True, va="bottom")
    dia = diamond(ax, 86, 27, 40, 19,
                  "class auto-approved?\nautoElevate manifest + signed + secure dir\nor HKCR\\CLSID\\{...}\\Elevation\\Enabled",
                  fs=8.5)
    elbow(ax, [app.side("r"), (76, 42), (76, 36.5), dia.side("t", 0.28)],
          kind="normal", label="create\nelevated object", li=0, t=0.6,
          lx=8.5, fs=8.5)
    good = box(ax, 116, 27, 16, 8, "consent UI\n(prompt)", fs=9)
    arrow(ax, dia.side("r"), good.side("l"), kind="normal", label="no",
          ly=1.3, va="bottom")
    bad = box(ax, 86, 8, 46, 9,
              "no prompt — silent high-IL object\nUAC bypass primitive",
              fs=9, edge=ATTACK, lw=2.2, tc=ATTACK)
    arrow(ax, dia.side("b"), bad.side("t"), kind="attack",
          label="yes — auto-approved", lx=17.5, fs=8.5, lc=ATTACK)
    return save(fig, "d07-elevation-moniker.png")


def d08():
    fig, ax = canvas("COM attack-surface map", w=12.5, h=7.0,
                     caption="every edge is an abuse primitive — red ticks mark the technique class")
    hub = box(ax, 62, 34, 24, 10, "combase / ole32", fs=10, weight="bold",
              dark=True)
    reg = box(ax, 22, 55, 26, 9, "Registry HKCR", fs=9.5)
    scm = box(ax, 102, 55, 24, 9, "SCM / RPCSS", fs=9.5)
    rot = box(ax, 16, 34, 24, 9, "ROT + monikers", fs=9.5)
    tlb = box(ax, 108, 34, 30, 9, "Type libraries\n+ proxy/stub DLLs", fs=9)
    dll = box(ax, 18, 11, 20, 8, "In-proc DLL", fs=9)
    sur = box(ax, 47, 11, 22, 8, "dllhost surrogate", fs=9)
    svc = box(ax, 78, 11, 24, 8, "Service-hosted EXE", fs=9)
    dcm = box(ax, 110, 11, 22, 8, "Remote DCOM", fs=9)

    a1 = (hub.side("t", 0.22), reg.side("b", 0.55))
    arrow(ax, a1[0], a1[1], kind="normal", label="CLSID resolution (HKCR)",
          t=0.45, lx=1.4, ly=4.0, fs=8.5, mono=True)
    tick(ax, a1[0], a1[1], "hijack", off=(-1.5, -4.0), t=0.6)
    a2 = (hub.side("t", 0.78), scm.side("b", 0.45))
    arrow(ax, a2[0], a2[1], kind="normal", label="activation RPC", t=0.45,
          lx=-1.2, ly=3.3, fs=8.5, mono=True)
    tick(ax, a2[0], a2[1], "impersonate", off=(3.0, -3.5), t=0.6)
    a3 = (hub.side("l"), rot.side("r"))
    arrow(ax, a3[0], a3[1], kind="normal", label="CoGetObject · ROT query",
          ly=1.4, fs=8.5, mono=True, va="bottom")
    tick(ax, a3[0], a3[1], "elevate", off=(0, -3.4), t=0.5)
    a4 = (hub.side("r"), tlb.side("l"))
    arrow(ax, a4[0], a4[1], kind="normal", label="marshaling metadata",
          ly=1.4, fs=8.5, mono=True, va="bottom")
    tick(ax, a4[0], a4[1], "marshal", off=(0, -3.4), t=0.5)
    a5 = (hub.side("b", 0.12), dll.side("t", 0.8))
    arrow(ax, a5[0], a5[1], kind="normal", label="LoadLibrary", t=0.5,
          lx=-1.3, ly=2.7, fs=8.5, mono=True)
    a6 = (hub.side("b", 0.38), sur.side("t", 0.6))
    arrow(ax, a6[0], a6[1], kind="normal", label="dllhost /Processid", t=0.55,
          lx=-2.5, ly=1.8, fs=8.5, mono=True)
    a7 = (hub.side("b", 0.55), svc.side("t", 0.5))
    arrow(ax, a7[0], a7[1], kind="normal", label="exe / service start", t=0.5,
          lx=1.7, ly=1.8, fs=8.5, mono=True)
    a8 = (hub.side("b", 0.90), dcm.side("t", 0.2))
    arrow(ax, a8[0], a8[1], kind="normal", label="TCP 135 + OXID", t=0.45,
          lx=1.0, ly=2.3, fs=8.5, mono=True)
    tick(ax, a8[0], a8[1], "relay", off=(1.2, -3.2), t=0.55)
    return save(fig, "d08-com-architecture.png")


ALL = [d01, d02, d03, d04, d05, d06, d07, d08]
