Bad Apple 但它是 Traceroute
Bad Apple but It's Traceroute

原始链接: https://jssfr.de/2026-07-27-bad-apple-but-traceroute.html

受在非常规硬件上播放《Bad Apple》这一趋势的启发,作者成功通过 `traceroute` 的输出结果呈现了该视频。 为了实现这一目标,作者使用 `nftables` 对 IPv6 ICMP 响应进行了操纵。通过利用 `numgen` 功能,系统可以动态更改 ICMPv6 数据包的源地址以代表不同的像素。为确保播放流畅,作者禁用了内核的 ICMPv6 速率限制,并对 `mtr` 诊断工具应用了自定义补丁,以防止其将多个地址聚类为不同的网络路径。 视频帧经过重采样,缩放至 30x11 像素的分辨率,并通过自定义 Python 脚本转换为庞大的 `nftables` 规则集。该项目凸显了《Bad Apple》作为非传统显示媒介基准的地位,特别是在那些仅限于低分辨率或二进制(开/关)像素数据的设备上。通过将网络诊断工具转变为创意显示媒介,作者为《Bad Apple》的众多硬件破解案例增添了又一个独特的作品。

抱歉。
相关文章

原文

As a follow-up to my post about how to make traceroute tools show arbitrary content and inspired by the release of another Bad Apple cover the other day I simply had to.

Of course, I'm not the first one to do this. That couldn't stop me though.

How it works

In the post I linked above, I had demonstrated how to inject fake hops into traceroute output.

Using the numgen feature of nftables, we can make the hops vary every time an ICMPv6 packet is generated. numgen gets us either random numbers or a monotonic counter. With a counter, we can easily make each hop return a different IPv6 address each time a response packet is generated. Using the playground from the other post:

ip netns exec tracemess nft -f - <<EOF
destroy table inet tracemess

table inet tracemess {
    chain prerouting {
        type filter hook prerouting priority raw;
        ip6 daddr fd00::1 ip6 hoplimit 1 reject with icmpv6 admin-prohibited;
    }

    chain postrouting {
        type filter hook postrouting priority raw;
        icmpv6 type destination-unreachable icmpv6 code admin-prohibited ip6 saddr fd00::1 @th,120,8 1 ip6 saddr set numgen inc mod 3 map { 0: fd00::2, 1: fd00::3, 2: fd00::4 } icmpv6 type set 3 icmpv6 code set 0 accept;
    }
}
EOF

Two other things were needed to make this happen. First of all, we need to disable the kernel's rate limit (by default, 1/s) for ICMPv6 egress using sysctl net.ipv6.icmp.ratelimit=0, otherwise the party will be over really quick.

The second problem is that mtr will normally show multiple addresses for each hop, because that indicates that multiple different paths are in use for a packet and that is normally useful information. In this case, however, that's rather annoying:

In order to fix that, we have to apply a one-line patch to mtr:

diff --git a/ui/net.c b/ui/net.c
index c0cbf28..7c52710 100644
--- a/ui/net.c
+++ b/ui/net.c
@@ -266,6 +266,7 @@ static void net_process_ping(
                 break; /* Found first vacant position */
             }
         }
+        i = 0;

         if (found == 0 && i < MAX_PATH) {
             memcpy(&nh->addrs[i], &addrcopy, sockaddr_addr_size(sourcesockaddr));

With all this in place, I used ffmpeg to resample the video to 8 frames-per-second (which equates an interval of 125 ms between frames) and export scaled-down (to 30x11 pixels) individual frames to PNG files. I then wrote a python script to read the image files and convert them into an nftables ruleset to generate the corresponding ICMPv6 responses.

That results in a bit over a megabyte of nftables rules, but it's definitely worth it.

Why?

The shadow art Bad Apple music video has become the benchmark for hacked displays, for a very loose definition of "display", similar to how running Doom (the 1993 video game) has become the benchmark of gaining code execution on a new hardware platform. I suspect the reason for that is that, due to its shadow art style, the video can be rendered even on "displays" which only have one bit per pixel (on/off).

There is a subreddit dedicated to showing off the kinds of devices people have gotten Bad Apple to display on. My favourite would at this point be this hack abusing a HD47780-compatible character LCD, simply because I enjoy hacking these devices, too. There's also more than one compilation video showing multiple versions.

Considering that … I simply had no choice, did I?

联系我们 contact @ memedata.com