Nach all den Jahren habe ich herausbekommen wo das Boot Logo liegt. Anhand meines KW 3.60 Images
24.06.2026
Bilder ab heute 6 Monate gültig - Start 24.06.2026
Box gestartet, Einzelne Partition auslesen, Kernel
Auf den Usbstick ist nun die kernel_20260616_1545.img
die .img kann man mit Winrar entpacken, Danach hat man eine kernel_20260616_1545 Datei
Diese Datei mit einen Hexeditor öffnen. bei mir Hex$ 47999C bis 5CB19C
Boot Logo ist ein ASCII-komprimiertes GIMP-Header-Format (GIMP RGB x-pixmap Derivat)
Suche nach Hex$: 21212121212121212121212121212121212121212121212121212121 usw.
bis zum letzten Hex$: 21
der punkt rechts gehört nicht mehr zum Bild: !!!!!!!!!!!!./opt/sdk/target/src/sd/os/oslinux/comps
Also alles in eine neue Datei speichern logo_extract.raw
Diese kann man mit den Pyhton Script in eine png umwandeln.
Code wurde mit Hilfe der google.com Ki Suche erstellt, und von mir getestet
-
[+] Spoiler
logo_extract-raw2png.py
Code: Alles auswählen
import os
import numpy as np
from PIL import Image
def run_honest_reconstruction():
current_dir = r"C:\Spiele"
raw_path = os.path.join(current_dir, "logo_extract.raw")
final_output_path = os.path.join(current_dir, "bootlogo_mathematisch_perfekt.png")
if not os.path.exists(raw_path):
print(f"FEHLER: 'logo_extract.raw' fehlt im Ordner {current_dir}!")
return
width, height = 720, 480
with open(raw_path, 'rb') as f:
raw_data = bytearray(f.read())
print(f"Lese Rohdatei: {os.path.basename(raw_path)}")
print("Führe originalen Neutrino-Decompressor aus...")
perfect_rgb = np.zeros((height, width, 3), dtype=np.uint8)
idx = 0
for y in range(height):
for x in range(width):
if idx + 3 >= len(raw_data):
break
# 1. Den Offset von 33 (0x21) abziehen, um die echten Bit-Werte zu erhalten
# Falls ein Byte kleiner als 33 ist (Fehlspannung/Fehler), fangen wir es ab
d0 = max(0, raw_data[idx] - 33)
d1 = max(0, raw_data[idx + 1] - 33)
d2 = max(0, raw_data[idx + 2] - 33)
d3 = max(0, raw_data[idx + 3] - 33)
# 2. Das originale NI-Splash-Makro 1:1 in Python übersetzt
r = (((d0) << 2) | ((d1) >> 4)) & 0xFF
g = ((((d1) & 0x0F) << 4) | ((d2) >> 2)) & 0xFF
b = ((((d2) & 0x03) << 6) | d3) & 0xFF
# Pixel schreiben
perfect_rgb[y, x] = [r, g, b]
idx += 4
# Finales Bild abspeichern
final_img = Image.fromarray(perfect_rgb, 'RGB')
final_img.save(final_output_path)
print(f"\nERFOLG! Das ehrliche Bootlogo wurde extrahiert:\n{final_output_path}")
if __name__ == "__main__":
run_honest_reconstruction()
-
[+] Spoiler
png2logo_extract-raw.py
Code: Alles auswählen
import os
import numpy as np
from PIL import Image
def generate_neutrino_raw():
current_dir = r"C:\Spiele"
input_image_path = os.path.join(current_dir, "new_splash.png")
output_raw_path = os.path.join(current_dir, "logo_extract.raw")
if not os.path.exists(input_image_path):
print(f"FEHLER: '{os.path.basename(input_image_path)}' fehlt in {current_dir}!")
return
# Bild laden und Dimensionen prüfen
img = Image.open(input_image_path).convert('RGB')
width, height = img.size
if width != 720 or height != 480:
print(f"WARNUNG: Bildgröße ist {width}x{height}. Erzwinge Resizing auf 720x480...")
img = img.resize((720, 480), Image.Resampling.LANCZOS)
width, height = 720, 480
img_np = np.array(img)
print("Kodiere RGB-Pixel in binäre Hardware-Struktur (inkl. Offset +33 / Hex 0x21)...")
# Ausgabearray vorbereiten: 720 * 480 Pixel * 4 Bytes pro Pixel
raw_bytes = bytearray()
for y in range(height):
for x in range(width):
r, g, b = img_np[y, x]
# --- DIE REINE BINÄR-KODIERUNG DES NEUTRINO-MAKROS ---
# 1. Aufteilung der 8-Bit-Farbkanäle in die vier 6-Bit-Pakete
d0 = (r >> 2)
d1 = ((r & 0x03) << 4) | (g >> 4)
d2 = ((g & 0x0F) << 2) | (b >> 6)
d3 = (b & 0x3F)
# 2. Addieren des Offsets von 33 (Hex 0x21) für jedes Byte
# 3. Absichern, dass sich die Werte im Byte-Bereich (0-255) bewegen
raw_bytes.append(max(0, min(255, d0 + 33)))
raw_bytes.append(max(0, min(255, d1 + 33)))
raw_bytes.append(max(0, min(255, d2 + 33)))
raw_bytes.append(max(0, min(255, d3 + 33)))
# Die kompilierte Byte-Struktur direkt binär auf die Festplatte schreiben
with open(output_raw_path, "wb") as f:
f.write(raw_bytes)
print(f"\nERFOLG! Die Binärdatei wurde fehlerfrei geschrieben:\n{output_raw_path}")
print(f"Dateigröße: {len(raw_bytes)} Bytes (Exakt {width}x{height}x4)")
print("Sie können die Datei jetzt im Hex-Editor prüfen oder direkt auf die Set-Top-Box flashen.")
if __name__ == "__main__":
generate_neutrino_raw()
-
[+] Spoiler
png2ni-splash_neu-h(zum Image Bauen)
Code: Alles auswählen
import os
import numpy as np
from PIL import Image
def generate_neutrino_header():
current_dir = r"C:\Spiele"
input_image_path = os.path.join(current_dir, "new_splash.png")
output_header_path = os.path.join(current_dir, "ni-splash_neu.h")
if not os.path.exists(input_image_path):
print(f"FEHLER: '{os.path.basename(input_image_path)}' fehlt in {current_dir}!")
return
# Bild laden und auf exakte Dimensionen prüfen
img = Image.open(input_image_path).convert('RGB')
width, height = img.size
if width != 720 or height != 480:
print(f"WARNUNG: Bildgröße ist {width}x{height}. Erzwinge Resizing auf 720x480...")
img = img.resize((720, 480), Image.Resampling.LANCZOS)
width, height = 720, 480
img_np = np.array(img)
print("Kodiere Bildstruktur in das proprietäre GIMP-ASCII-Format...")
# Jedes Pixel benötigt 4 Bytes im Header für 3 RGB-Kanäle
encoded_bytes = bytearray()
for y in range(height):
for x in range(width):
r, g, b = img_np[y, x]
# --- DER GEGEN-ALGORITHMUS ZUM NEUTRINO-MAKRO ---
# Wir verteilen die 8-Bit-Farbkanäle auf vier 6-Bit-Blöcke
# und addieren 33 (+0x21), um Nullbytes im C-String zu verhindern.
d0 = (r >> 2) + 33
d1 = (((r & 0x03) << 4) | (g >> 4)) + 33
d2 = (((g & 0x0F) << 2) | (b >> 6)) + 33
d3 = (b & 0x3F) + 33
# Sicherheits-Check: Begrenzung auf gültigen ASCII-Bereich
encoded_bytes.append(max(33, min(255, d0)))
encoded_bytes.append(max(33, min(255, d1)))
encoded_bytes.append(max(33, min(255, d2)))
encoded_bytes.append(max(33, min(255, d3)))
print("Generiere C-Headerdatei (ni-splash_neu.h)...")
# Erstellung der originalen NI-Quellcodestruktur
with open(output_header_path, "w", encoding="ascii") as f:
f.write("/* GIMP header image file format (RGB): Generiert via Python Encoder */\n\n")
f.write(f"static unsigned int width = {width};\n")
f.write(f"static unsigned int height = {height};\n\n")
f.write("#define HEADER_PIXEL(data,pixel) {\\\n")
f.write("pixel[0] = (((data[0] - 33) << 2) | ((data[1] - 33) >> 4)); \\\n")
f.write("pixel[1] = ((((data[1] - 33) & 0xF) << 4) | ((data[2] - 33) >> 2)); \\\n")
f.write("pixel[2] = ((((data[2] - 33) & 0x3) << 6) | ((data[3] - 33))); \\\n")
f.write("data += 4; \\\n")
f.write("}\n\n")
f.write("static char *header_data =\n")
# Um den GCC-Compiler nicht zu überlasten, brechen wir den String
# zeilenweise nach jeweils 64 ASCII-Zeichen (16 Pixeln) um.
line_length = 64
total_len = len(encoded_bytes)
for i in range(0, total_len, line_length):
chunk = encoded_bytes[i:i+line_length]
# Konvertiert die codierten Bytes in lesbare ASCII-Zeichen (z.B. "!!!!")
ascii_line = chunk.decode('ascii', errors='replace')
# Maskierung von potenziellen C-String-Gefahren wie Backslash oder Anführungszeichen
ascii_line = ascii_line.replace('\\', '\\\\').replace('"', '\\"')
f.write(f'\t"{ascii_line}"\n')
f.write('\t"";\n')
print(f"\nERFOLG! Der C-Header wurde fehlerfrei geschrieben:\n{output_header_path}")
print("Sie können den Inhalt nun direkt in den Neutrino-Sourcecode einbauen.")
if __name__ == "__main__":
generate_neutrino_header()
-
[+] Spoiler
kernel_20260616_1545_img eigenes Logo einbauen neu_logo.bin
(Achtung Kernel muss gleich gross bleiben) Bitgleich sonst kein Booten der Box mehr.
Code: Alles auswählen
import struct
import zlib
import gzip
import sys
# Dateinamen definieren
ORIGINAL_IMG = "kernel_20260616_1545.img"
NEW_LOGO_FILE = "neu_logo.bin"
OUTPUT_IMG = "kernel_patched.img"
# Offsets für das Boot-Logo im unkomprimierten vmlinux
LOGO_START = 0x47999C
LOGO_END = 0x5CB19C
LOGO_SIZE = LOGO_END - LOGO_START # 1.382.400 Bytes
MAX_PARTITION_SIZE = 3145728 # 3 MB Flash-Limit der Coolstream Tank
def pause():
input("\n[PAUSE] Bitte Enter-Taste drücken, um fortzufahren...")
print("-" * 50)
def patch_and_rebuild():
print("[*] Lese originales U-Boot Image ein...")
try:
with open(ORIGINAL_IMG, "rb") as f:
orig_data = f.read()
except FileNotFoundError:
print(f"[-] Fehler: '{ORIGINAL_IMG}' nicht gefunden!")
return
# 1. U-Boot Header prüfen
header = orig_data[:64]
magic = struct.unpack(">I", header[0:4])
if magic != (0x27051956,):
if magic != 0x27051956:
print("[-] Fehler: Keine gültige U-Boot-Datei!")
return
print("[+] Schritt 1: Gültigen U-Boot Header gefunden.")
pause()
# 2. Hardcodierte Offsets anwenden
HEADER_OFFSET = 64
GZIP_REL_OFFSET = 26840
FDT_REL_OFFSET = 3007672
FDT_SIZE = 5712
gzip_start = HEADER_OFFSET + GZIP_REL_OFFSET
fdt_start = HEADER_OFFSET + FDT_REL_OFFSET
# 3. Komponenten isolieren und auf Festplatte schreiben
print("[*] Isoliere Firmware-Komponenten (ARM-Stub, Gzip-Kernel, FDT)...")
zimage_stub = orig_data[HEADER_OFFSET:gzip_start]
fdt_data = orig_data[fdt_start : fdt_start + FDT_SIZE]
compressed_core_orig = orig_data[gzip_start:fdt_start]
print("[*] Schreibe isolierte Komponenten als Dateien auf die Festplatte...")
with open("arm_boot_stub.bin", "wb") as f:
f.write(zimage_stub)
with open("kernel_core_compressed.gz", "wb") as f:
f.write(compressed_core_orig)
with open("coolstream.dtb", "wb") as f:
f.write(fdt_data)
print(f"[+] 'arm_boot_stub.bin' gespeichert ({len(zimage_stub)} Bytes)")
print(f"[+] 'kernel_core_compressed.gz' gespeichert ({len(compressed_core_orig)} Bytes)")
print(f"[+] 'coolstream.dtb' gespeichert ({len(fdt_data)} Bytes)")
print("[+] Schritt 2: Alle originalen Blöcke erfolgreich getrennt und exportiert.")
pause()
# 4. Den Kernel-Core im Arbeitsspeicher dekomprimieren und als Datei sichern
print("[*] Dekomprimiere Kernel-Core (vmlinux)...")
try:
vmlinux = bytearray(gzip.decompress(compressed_core_orig))
except Exception:
try:
print("[*] Standard-Gzip fordert Footer. Wechsle auf toleranten zlib-Dekomprimierer...")
decompressor = zlib.decompressobj(16 + zlib.MAX_WBITS)
vmlinux = bytearray(decompressor.decompress(compressed_core_orig))
except Exception as e:
print(f"[-] Kritischer Fehler beim Dekomprimieren: {e}")
return
print(f"[+] 'vmlinux' erfolgreich dekomprimiert ({len(vmlinux)} Bytes).")
print("[*] Speichere unkomprimierten Kernel auf der Festplatte...")
with open("vmlinux_entpackt.bin", "wb") as f:
f.write(vmlinux)
print("[+] 'vmlinux_entpackt.bin' gespeichert.")
print("[+] Schritt 3: Der Kernel-Code liegt nun entpackt im RAM und als Datei vor.")
pause()
# 5. Neues Boot-Logo einlesen
print("[*] Lese neues Boot-Logo ein...")
try:
with open(NEW_LOGO_FILE, "rb") as f:
new_logo = f.read()
except FileNotFoundError:
print(f"[-] Fehler: '{NEW_LOGO_FILE}' nicht gefunden!")
return
if len(new_logo) != LOGO_SIZE:
print(f"[-] Fehler: Das Logo hat die falsche Größe ({len(new_logo)} Bytes). MUSS exakt {LOGO_SIZE} sein!")
return
print(f"[+] Neues Boot-Logo geladen und Größe verifiziert ({len(new_logo)} Bytes).")
pause()
# 6. Boot-Logo austauschen
print(f"[*] Ersetze Boot-Logo von Offset {hex(LOGO_START)} bis {hex(LOGO_END)}...")
vmlinux[LOGO_START:LOGO_END] = new_logo
print("[+] Schritt 4: Das neue Boot-Logo wurde in den Kernel-Code injiziert.")
pause()
# 7. Neu komprimieren mit maximaler Stufe 9
print("[*] Komprimiere modifizierten Kernel neu (Gzip Max)...")
compressed_core_new = gzip.compress(vmlinux, compresslevel=9)
print(f"[+] Neuer Kernel-Core komprimiert ({len(compressed_core_new)} Bytes).")
print("[+] Schritt 5: Kernel mit integriertem neuen Bild erfolgreich komprimiert.")
pause()
# 8. Das neue zImage zusammensetzen
print("[*] Setze das neue zImage-Paket zusammen...")
new_zimage = bytearray()
new_zimage.extend(zimage_stub)
new_zimage.extend(compressed_core_new)
new_zimage.extend(fdt_data)
# Größencheck
total_file_size = len(new_zimage) + 64
print(f"[*] Berechnete finale Dateigröße: {total_file_size} Bytes")
is_too_large = total_file_size > MAX_PARTITION_SIZE
if is_too_large:
print("\n[!!!] WARNUNG: DAS IMAGE IST ZU GROSS FÜR DEN FLASH-SPEICHER! [!!!]")
print(f"Größe: {total_file_size} von maximal {MAX_PARTITION_SIZE} Bytes.")
print(f"Es fehlen {total_file_size - MAX_PARTITION_SIZE} Bytes Platz.")
print("-> Das Erstellen wird auf deinen Wunsch hin TROTZDEM fortgesetzt.")
print("-> VORSICHT: Dieses Image kann die Box beim Flashen beschädigen!")
else:
print("[+] Schritt 6: Größencheck erfolgreich! Datei passt in den Flash der Tank.")
# Mit Nullen auffüllen bis exakt zur Partitionsgröße (3 MB), falls noch Platz ist
padding_needed = MAX_PARTITION_SIZE - total_file_size
new_zimage.extend(b"\x00" * padding_needed)
pause()
# 9. U-Boot Header updaten und CRCs neu berechnen
print("[*] Berechne U-Boot Header und CRC32-Prüfsummen neu...")
new_header = bytearray(header)
new_header[12:16] = struct.pack(">I", len(new_zimage))
data_crc = zlib.crc32(new_zimage) & 0xFFFFFFFF
new_header[24:28] = struct.pack(">I", data_crc)
new_header[4:8] = b"\x00\x00\x00\x00"
header_crc = zlib.crc32(new_header) & 0xFFFFFFFF
new_header[4:8] = struct.pack(">I", header_crc)
print("[+] Schritt 7: Alle CRC-Prüfsummen im U-Boot Header aktualisiert.")
pause()
# 10. Finale Datei schreiben
print(f"[*] Schreibe neues Image nach '{OUTPUT_IMG}'...")
with open(OUTPUT_IMG, "wb") as f:
f.write(new_header)
f.write(new_zimage)
if is_too_large:
print(f"\n[!] Fertig mit WARNUNG! '{OUTPUT_IMG}' wurde mit {total_file_size} Bytes erstellt (Limit war {MAX_PARTITION_SIZE}).")
else:
print(f"\n[+++] Erfolg! '{OUTPUT_IMG}' hat exakt {MAX_PARTITION_SIZE} Bytes und ist bereit zum Flashen.")
if __name__ == "__main__":
patch_and_rebuild()