#!/usr/bin/env python3
PK     ì]ÜÎ)Ő  Ő  	   worker.py#!/usr/bin/env python3
"""Privileged, non-interactive LKEUSB writer. Input only from the GUI."""
import json, os, re, shutil, subprocess, sys, tempfile
def out(s): print(s, flush=True)
def run(*args): out("$ " + " ".join(map(str,args))); subprocess.run(args,check=True)
def fail(msg): out("FEHLER: "+msg); sys.exit(1)
def valid_device(d):
    return re.fullmatch(r"/dev/[a-zA-Z0-9._-]+",d) and os.path.exists(d) and os.stat(d).st_mode & 0o170000 == 0o060000
def is_removable_or_usb(d):
    info=json.loads(subprocess.check_output(["lsblk","--json","-o","RM,TRAN,TYPE",d],text=True))["blockdevices"][0]
    return info.get("type") == "disk" and (bool(info.get("rm")) or info.get("tran") == "usb")
def unmount(d):
    raw=subprocess.check_output(["lsblk","-lnpo","PATH,MOUNTPOINT",d],text=True)
    mounts=[line.split(maxsplit=1)[1] for line in raw.splitlines() if len(line.split(maxsplit=1))==2 and line.split(maxsplit=1)[1]]
    for m in reversed(mounts): run("umount",m)
def direct(p):
    out("Schreibe Hybrid-/Raw-Abbild auf das GerĂ€t âŠ")
    run("dd",f"if={p['iso']}",f"of={p['device']}","bs=4M","status=progress","conv=fsync")
    if p.get("verify"):
        out("Verifiziere die ersten Bytes des Abbilds âŠ")
        size=os.path.getsize(p["iso"]); a=subprocess.check_output(["sha256sum",p["iso"]],text=True).split()[0]
        reader=subprocess.Popen(["head","-c",str(size),p["device"]],stdout=subprocess.PIPE)
        b=subprocess.check_output(["sha256sum"],stdin=reader.stdout,text=True).split()[0]
        reader.wait()
        if a!=b: fail("Verifikation fehlgeschlagen â Stick nicht verwenden.")
def write_windows_bypass(destination):
    """Setup reads this at boot and applies LabConfig before compatibility checks."""
    content = '''<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
  <settings pass="windowsPE">
    <component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
      <RunSynchronous>
        <RunSynchronousCommand wcm:action="add" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
          <Order>1</Order><Path>reg add HKLM\\SYSTEM\\Setup\\LabConfig /v BypassTPMCheck /t REG_DWORD /d 1 /f</Path><Description>TPM compatibility override</Description>
        </RunSynchronousCommand>
        <RunSynchronousCommand wcm:action="add" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
          <Order>2</Order><Path>reg add HKLM\\SYSTEM\\Setup\\LabConfig /v BypassSecureBootCheck /t REG_DWORD /d 1 /f</Path><Description>Secure Boot compatibility override</Description>
        </RunSynchronousCommand>
        <RunSynchronousCommand wcm:action="add" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
          <Order>3</Order><Path>reg add HKLM\\SYSTEM\\Setup\\LabConfig /v BypassRAMCheck /t REG_DWORD /d 1 /f</Path><Description>RAM compatibility override</Description>
        </RunSynchronousCommand>
        <RunSynchronousCommand wcm:action="add" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
          <Order>4</Order><Path>reg add HKLM\\SYSTEM\\Setup\\LabConfig /v BypassCPUCheck /t REG_DWORD /d 1 /f</Path><Description>CPU compatibility override</Description>
        </RunSynchronousCommand>
      </RunSynchronous>
    </component>
  </settings>
</unattend>'''
    with open(os.path.join(destination, "autounattend.xml"), "w", encoding="utf-8") as output: output.write(content)
def windows(p):
    for c in ("parted","mkfs.vfat","mount","rsync"):
        if not shutil.which(c): fail(f"BenĂ¶tigtes Programm fehlt: {c}")
    iso_dir=tempfile.mkdtemp(prefix="lkeusb-iso-"); usb_dir=tempfile.mkdtemp(prefix="lkeusb-usb-")
    try:
        out("Erstelle UEFI-kompatible FAT32-Partition âŠ")
        run("parted","-s",p["device"],"mklabel","gpt" if p["scheme"].startswith("GPT") else "msdos")
        run("parted","-s",p["device"],"mkpart","primary","fat32","1MiB","100%")
        run("partprobe",p["device"]); import time; time.sleep(1)
        parts=subprocess.check_output(["lsblk","-nrpo","PATH,TYPE",p["device"]],text=True).splitlines()
        candidates=[line.rsplit(" ",1)[0] for line in parts if line.endswith(" part")]
        if not candidates: fail("Die neue USB-Partition wurde nicht erkannt.")
        part=candidates[0]
        run("mkfs.vfat","-F","32","-n",p["label"][:11],part); run("mount",p["iso"],iso_dir,"-o","loop,ro"); run("mount",part,usb_dir)
        wim=os.path.join(iso_dir,"sources","install.wim")
        if os.path.isfile(wim) and os.path.getsize(wim)>4*1024**3:
            if not shutil.which("wimlib-imagex"): fail("install.wim ist grĂ¶Ăer als FAT32 erlaubt. Installiere 'wimtools' und starte erneut.")
            out("Teile groĂe Windows-Installationsdatei auf âŠ")
            run("rsync","-aH","--exclude=/sources/install.wim",iso_dir+"/",usb_dir+"/")
            run("wimlib-imagex","split",wim,os.path.join(usb_dir,"sources","install.swm"),"3800")
        else: run("rsync","-aH",iso_dir+"/",usb_dir+"/")
        if p.get("win_bypass"):
            write_windows_bypass(usb_dir)
            out("Windows-11-KompatibilitĂ€tsoptionen (TPM/Secure Boot/CPU/RAM) hinzugefĂŒgt.")
        run("sync"); out("Windows-Installationsstick fertig.")
    finally:
        for d in (usb_dir,iso_dir):
            subprocess.run(["umount",d],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL)
            os.rmdir(d)
def main():
    if os.geteuid()!=0: fail("Administratorrechte erforderlich.")
    try: p=json.loads(sys.argv[1])
    except Exception: fail("UngĂŒltige Auftragsdaten.")
    if not os.path.isfile(p.get("iso","")) or not valid_device(p.get("device","")) or not is_removable_or_usb(p["device"]): fail("UngĂŒltige ISO oder kein zulĂ€ssiges USB-GerĂ€t.")
    # Refuse root/system disk even if a hostile GUI request reaches this process.
    rootdev=subprocess.check_output(["findmnt","-no","SOURCE","/"],text=True).strip()
    rootdisk=subprocess.check_output(["lsblk","-no","PKNAME",rootdev],text=True, stderr=subprocess.DEVNULL).strip()
    if p["device"] == rootdev or (rootdisk and p["device"] == "/dev/"+rootdisk): fail("Systemlaufwerk ist gesperrt.")
    unmount(p["device"])
    windows(p) if p.get("mode","").startswith("Windows") else direct(p)
if __name__=="__main__": main()
PK     ì]r
@Žá0  á0     app.py#!/usr/bin/env python3
"""LKEUSB: safe graphical bootable USB creator for Linux."""
import hashlib, json, os, queue, random, shutil, subprocess, sys, threading
import tkinter as tk
from tkinter import filedialog, messagebox, simpledialog, ttk

BASE = os.path.dirname(os.path.abspath(__file__))
# A standalone LKEUSB archive supplies this temporary worker location itself.
WORKER = os.environ.get("LKEUSB_WORKER", os.path.join(BASE, "worker.py"))

def command_exists(name): return shutil.which(name) is not None

def disks():
    try:
        raw = subprocess.check_output(["lsblk", "--json", "-b", "-o", "NAME,PATH,SIZE,MODEL,TRAN,TYPE,RM,MOUNTPOINTS"], text=True)
        entries = json.loads(raw)["blockdevices"]
        # Many USB SSDs report RM=0; TRAN=usb still makes them a valid target.
        return [d for d in entries if d["type"] == "disk" and (d.get("rm") or d.get("tran") == "usb") and not d["path"].startswith("/dev/loop")]
    except Exception: return []

class LKEUSB(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("LKEUSB â Bootable USB Creator")
        self.geometry("900x700"); self.minsize(790, 630)
        self.iso = tk.StringVar(); self.device = tk.StringVar(); self.mode = tk.StringVar(value="Auto erkennen")
        self.scheme = tk.StringVar(value="GPT (UEFI)"); self.label = tk.StringVar(value="LKEUSB")
        self.verify = tk.BooleanVar(value=True); self.win_bypass = tk.BooleanVar(value=True); self.sha = tk.StringVar(); self.busy = False; self.logs = queue.Queue()
        self.pulse = 0; self.orbs = []; self.wave = []
        self._style(); self._build(); self.refresh(); self.after(120, self.pump); self.after(80, self.animate)

    def _style(self):
        s = ttk.Style(self); s.theme_use("clam")
        self.configure(bg="#0b1020")
        s.configure("TFrame", background="#0b1020"); s.configure("TLabel", background="#0b1020", foreground="#dbeafe", font=("Sans", 10))
        s.configure("Title.TLabel", foreground="#f8fbff", font=("Sans", 25, "bold")); s.configure("Sub.TLabel", foreground="#8da4c9", font=("Sans", 10))
        s.configure("TButton", padding=9, font=("Sans", 10, "bold"), background="#1e293b", foreground="#e5efff")
        s.map("TButton", background=[("active", "#334155")]); s.configure("Accent.TButton", background="#2563eb", foreground="white")
        s.map("Accent.TButton", background=[("active", "#3b82f6"), ("disabled", "#334155")]); s.configure("TEntry", fieldbackground="#f8fafc")
        s.configure("TLabelframe", background="#0b1020", foreground="#67e8f9", bordercolor="#233253")
        s.configure("TLabelframe.Label", background="#0b1020", foreground="#67e8f9", font=("Sans", 10, "bold"))
        s.configure("Horizontal.TProgressbar", troughcolor="#17213a", background="#22d3ee", bordercolor="#17213a")

    def _build(self):
        pad = {"padx": 18, "pady": 7}; root = ttk.Frame(self, padding=18); root.pack(fill="both", expand=True)
        hero=tk.Frame(root, bg="#101a34", height=112, highlightbackground="#263b6b", highlightthickness=1); hero.pack(fill="x", pady=(0,14)); hero.pack_propagate(False)
        self.motion=tk.Canvas(hero, bg="#101a34", highlightthickness=0, height=110); self.motion.place(relx=0, rely=0, relwidth=1, relheight=1)
        self.motion.bind("<Configure>", self.draw_motion_base)
        self.hero_title=tk.Label(hero, text="â  LKEUSB", bg="#101a34", fg="#f8fbff", font=("Sans",25,"bold")); self.hero_title.place(x=22,y=20)
        tk.Label(hero, text="BOOTABLE MEDIA STUDIO  âą  WINDOWS & LINUX", bg="#101a34", fg="#8da4c9", font=("Sans",10,"bold")).place(x=29,y=63)
        self.live=tk.Label(hero, text="â  MEDIA ENGINE BEREIT", bg="#101a34", fg="#22d3ee", font=("Sans",9,"bold")); self.live.place(relx=.975,rely=.5,anchor="e")
        src = ttk.LabelFrame(root, text="1  ISO-Abbild auswĂ€hlen", padding=12); src.pack(fill="x")
        ttk.Entry(src, textvariable=self.iso).pack(side="left", fill="x", expand=True, padx=(0,8))
        ttk.Button(src, text="ISO wĂ€hlenâŠ", command=self.pick_iso).pack(side="left")
        ttk.Label(root, text="SHA-256 (optional â prĂŒft die heruntergeladene ISO vor dem Schreiben):").pack(anchor="w", **pad)
        ttk.Entry(root, textvariable=self.sha).pack(fill="x", padx=18)
        target = ttk.LabelFrame(root, text="2  Ziel-USB auswĂ€hlen", padding=12); target.pack(fill="x", **pad)
        self.combo = ttk.Combobox(target, textvariable=self.device, state="readonly"); self.combo.pack(side="left", fill="x", expand=True, padx=(0,8))
        ttk.Button(target, text="â» Aktualisieren", command=self.refresh).pack(side="left")
        opt = ttk.LabelFrame(root, text="3  Installationsoptionen", padding=12); opt.pack(fill="x", padx=18, pady=7)
        row = ttk.Frame(opt); row.pack(fill="x")
        ttk.Label(row, text="Modus:").pack(side="left")
        ttk.Combobox(row, textvariable=self.mode, state="readonly", values=["Auto erkennen", "Linux / Hybrid-ISO (direkt schreiben)", "Windows-Installer (UEFI/FAT32)", "Raw-Image (direkt schreiben)"], width=35).pack(side="left", padx=8)
        ttk.Label(row, text="Partition:").pack(side="left", padx=(18,0))
        ttk.Combobox(row, textvariable=self.scheme, state="readonly", values=["GPT (UEFI)", "MBR (Legacy/UEFI-CSM)"], width=24).pack(side="left", padx=8)
        row2=ttk.Frame(opt); row2.pack(fill="x", pady=(10,0)); ttk.Label(row2,text="USB-Name:").pack(side="left")
        ttk.Entry(row2, textvariable=self.label, width=24).pack(side="left",padx=8)
        ttk.Checkbutton(row2, text="Nach dem Schreiben verifizieren (direktes Abbild)", variable=self.verify).pack(side="left", padx=12)
        row3=ttk.Frame(opt); row3.pack(fill="x", pady=(8,0))
        ttk.Checkbutton(row3, text="Windows 11: Hardware-PrĂŒfungen umgehen (TPM 2.0, Secure Boot, RAM & CPU)", variable=self.win_bypass).pack(side="left")
        ttk.Label(row3, text="  Nur beim Windows-Installer wirksam.", style="Sub.TLabel").pack(side="left")
        warn=ttk.Label(root, text="ACHTUNG: Das gewĂ€hlte Ziellaufwerk wird vollstĂ€ndig gelĂ¶scht. Interne Laufwerke werden absichtlich nicht angeboten.", foreground="#fbbf24", wraplength=780)
        warn.pack(anchor="w", **pad)
        self.progress=ttk.Progressbar(root, mode="indeterminate"); self.progress.pack(fill="x",padx=18,pady=(2,8))
        self.status=tk.StringVar(value="Bereit. ISO und USB-Stick auswĂ€hlen."); ttk.Label(root,textvariable=self.status).pack(anchor="w",padx=18)
        self.go=ttk.Button(root,text="BOOT-STICK ERSTELLEN",style="Accent.TButton",command=self.start); self.go.pack(fill="x",padx=18,pady=10)
        self.console=tk.Text(root,height=9,bg="#030712",fg="#d1d5db",insertbackground="white",relief="flat",state="disabled",font=("Monospace",9)); self.console.pack(fill="both",expand=True,padx=18)

    def animate(self):
        """Visible animated light paths and particles in the header."""
        self.pulse=(self.pulse+1)%360
        if self.motion.winfo_width() > 10:
            for orb in self.orbs:
                self.motion.move(orb[0], -orb[1], 0)
                coords=self.motion.coords(orb[0])
                if coords and coords[2] < 0:
                    width=self.motion.winfo_width(); y=random.randint(8,102)
                    self.motion.coords(orb[0], width+20, y, width+26, y+6)
            for index, line in enumerate(self.wave):
                x=(self.pulse*2 + index*95) % (self.motion.winfo_width()+150)-150
                self.motion.coords(line, x, 96, x+110, 34)
        phase=self.pulse % 32
        color="#a5f3fc" if phase < 16 else "#22d3ee"
        self.live.configure(fg=color, text="â  SCHREIBVORGANG LĂUFT" if self.busy else "â  MEDIA ENGINE BEREIT")
        self.after(32, self.animate)

    def draw_motion_base(self, _event=None):
        """Rebuild decoration only after the canvas receives its real size."""
        c=self.motion; c.delete("motion"); self.orbs=[]; self.wave=[]; width=c.winfo_width()
        for x in range(0, width, 44):
            c.create_line(x, 110, x+76, 0, fill="#17264a", width=1, tags="motion")
        for n in range(22):
            x=random.randint(0, max(1,width)); y=random.randint(5,105)
            item=c.create_oval(x,y,x+5,y+5,fill="#38bdf8",outline="",tags="motion")
            self.orbs.append((item, random.choice((1,1.4,1.8,2.2))))
        for n in range(5):
            item=c.create_line(-150+n*95,96,-40+n*95,34,fill="#1d4ed8",width=2,tags="motion")
            self.wave.append(item)
        # Keep textual elements above Canvas decorations.
        c.lower("motion")

    def pick_iso(self):
        path=filedialog.askopenfilename(title="ISO- oder Image-Datei auswĂ€hlen", filetypes=[("ISO/Image", "*.iso *.img *.raw"), ("Alle Dateien","*")])
        if path: self.iso.set(path); self.detect(path)
    def detect(self,path):
        n=os.path.basename(path).lower()
        self.mode.set("Windows-Installer (UEFI/FAT32)" if "windows" in n or "win10" in n or "win11" in n else "Linux / Hybrid-ISO (direkt schreiben)")
    def refresh(self):
        choices=[]
        for d in disks():
            size=int(d.get("size",0)); model=(d.get("model") or "Unbekannt").strip(); mounts=", ".join(x for x in d.get("mountpoints",[]) if x)
            choices.append(f"{d['path']}  â  {size/1e9:.1f} GB  â  {model}" + (f"  [eingehĂ€ngt: {mounts}]" if mounts else ""))
        self.combo["values"]=choices
        if choices: self.device.set(choices[0])
        else: self.device.set("")
    def log(self,s): self.logs.put(s)
    def pump(self):
        try:
            while True:
                s=self.logs.get_nowait(); self.console.config(state="normal"); self.console.insert("end",s+"\n"); self.console.see("end"); self.console.config(state="disabled")
        except queue.Empty: pass
        self.after(120,self.pump)
    def start(self):
        iso=self.iso.get().strip(); target=self.device.get().split("  â")[0].strip()
        if not os.path.isfile(iso): return messagebox.showerror("ISO fehlt","Bitte eine vorhandene ISO/Image-Datei wĂ€hlen.")
        if not target.startswith("/dev/"): return messagebox.showerror("USB fehlt","Bitte einen WechseldatentrĂ€ger wĂ€hlen.")
        mode=self.mode.get()
        if mode=="Auto erkennen": self.detect(iso); mode=self.mode.get()
        msg=(f"ALLE DATEN auf {target} werden unwiderruflich gelĂ¶scht.\n\nQuelle: {os.path.basename(iso)}\nModus: {mode}\n\nZum Fortfahren bitte den GerĂ€tenamen eingeben:")
        answer=simpledialog.askstring("LĂ¶schen bestĂ€tigen",msg,parent=self)
        if answer != os.path.basename(target): return messagebox.showinfo("Abgebrochen","BestĂ€tigung stimmt nicht â es wurde nichts geĂ€ndert.")
        if self.sha.get().strip():
            self.status.set("PrĂŒfe SHA-256 âŠ"); self.update_idletasks()
            hasher=hashlib.sha256()
            with open(iso,"rb") as source:
                for chunk in iter(lambda: source.read(4 * 1024 * 1024), b""): hasher.update(chunk)
            digest=hasher.hexdigest()
            if digest.lower()!=self.sha.get().strip().lower(): return messagebox.showerror("PrĂŒfsumme falsch",f"SHA-256 stimmt nicht ĂŒberein.\nErmittelt: {digest}")
        payload={"iso":os.path.realpath(iso),"device":target,"mode":mode,"scheme":self.scheme.get(),"label":self.label.get().strip() or "LKEUSB","verify":self.verify.get(),"win_bypass":self.win_bypass.get()}
        self.busy=True; self.go.config(state="disabled"); self.progress.start(12); self.status.set("Schreibvorgang lĂ€uft â Fenster offen lassen.")
        threading.Thread(target=self.run,args=(payload,),daemon=True).start()
    def run(self,payload):
        cmd=["pkexec",sys.executable,WORKER,json.dumps(payload)] if command_exists("pkexec") else ["sudo","-E",sys.executable,WORKER,json.dumps(payload)]
        self.log("Starte mit Administratorrechten âŠ")
        p=subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True,bufsize=1)
        for line in p.stdout: self.log(line.rstrip())
        rc=p.wait(); self.after(0,lambda:self.done(rc))
    def done(self,rc):
        self.busy=False; self.go.config(state="normal"); self.progress.stop()
        self.status.set("Fertig â USB-Stick kann sicher entfernt werden." if rc==0 else "Fehler oder Abbruch â Details im Protokoll.")
        messagebox.showinfo("LKEUSB", "Boot-Stick erfolgreich erstellt." if rc==0 else "Der Vorgang wurde nicht abgeschlossen. Bitte Protokoll prĂŒfen.")
if __name__=="__main__": LKEUSB().mainloop()
PK     ì]täv»	  »	     __main__.py#!/usr/bin/env python3
"""Entry point for the self-contained LKEUSB executable."""
import atexit
import os
import runpy
import shutil
import subprocess
import sys
import tempfile
import zipfile

def missing(command):
    return shutil.which(command) is None

def install_requirements():
    required = ["parted", "mkfs.vfat", "rsync", "wimlib-imagex", "lsblk", "dd", "sha256sum"]
    try:
        import tkinter  # noqa: F401
    except ImportError:
        required.append("python3-tk")
    if not any(missing(item) for item in required if item != "python3-tk") and "python3-tk" not in required:
        return
    print("LKEUSB installiert fehlende Laufzeitkomponenten âŠ", flush=True)
    packages = None
    if missing("python3"):
        raise RuntimeError("Python 3 fehlt und kann nicht aus einer Python-App nachinstalliert werden.")
    if shutil.which("apt-get"):
        packages = ["sudo", "apt-get", "install", "-y", "python3-tk", "util-linux", "coreutils", "polkit", "parted", "dosfstools", "rsync", "wimtools"]
    elif shutil.which("dnf"):
        packages = ["sudo", "dnf", "install", "-y", "python3-tkinter", "util-linux", "coreutils", "polkit", "parted", "dosfstools", "rsync", "wimlib"]
    elif shutil.which("pacman"):
        packages = ["sudo", "pacman", "-Sy", "--needed", "--noconfirm", "python", "tk", "util-linux", "coreutils", "polkit", "parted", "dosfstools", "rsync", "wimlib"]
    elif shutil.which("zypper"):
        packages = ["sudo", "zypper", "--non-interactive", "install", "python3-tk", "util-linux", "coreutils", "polkit", "parted", "dosfstools", "rsync", "wimlib"]
    if not packages:
        raise RuntimeError("Kein unterstĂŒtzter Paketmanager gefunden (APT, DNF, Pacman, Zypper).")
    subprocess.run(packages, check=True)

def main():
    try:
        install_requirements()
    except (RuntimeError, subprocess.CalledProcessError) as error:
        print("LKEUSB konnte seine AbhĂ€ngigkeiten nicht einrichten: " + str(error), file=sys.stderr)
        raise SystemExit(1)
    runtime = tempfile.mkdtemp(prefix="lkeusb-")
    atexit.register(lambda: shutil.rmtree(runtime, ignore_errors=True))
    with zipfile.ZipFile(sys.argv[0]) as archive:
        worker = os.path.join(runtime, "worker.py")
        with open(worker, "wb") as target:
            target.write(archive.read("worker.py"))
    os.chmod(worker, 0o755)
    os.environ["LKEUSB_WORKER"] = worker
    runpy.run_module("app", run_name="__main__")

if __name__ == "__main__":
    main()
PK     ì]ÜÎ)Ő  Ő  	           í   worker.pyPK     ì]r
@Žá0  á0             í  app.pyPK     ì]täv»	  »	             €J  __main__.pyPK      €   üS    