Friday, 17 April 2026

为什么修改 resolv.conf 无效

 

前提条件说明 本文讨论基于以下前提:
  • 你的系统使用了 systemd-resolved
  • 网络由 NetworkManager 或 Netplan 接管
  • /etc/resolv.conf 为动态生成的符号链接
  • 注意:不同发行版默认配置可能略有差异(如 nsswitch.conf 或 NetworkManager 设置)。
在默认使用 systemd-resolved + NetworkManager / Netplan 的现代 Linux 发行版中,/etc/resolv.conf 往往不是配置源,而是一个动态生成的解析接口。手动修改的内容通常无法跨越下一次网络重连或系统重启,因为经理人们随时准备根据最新的网络环境背刺你的修改。

 

0. DNS 解析的三层结构(核心心智模型)

需要明确的是,systemd-resolved 只是 glibc NSS 解析链条中的一个可选后端。在现代 Linux 系统中,一次 DNS 查询是一个严密的分层架构: A. 应用层:应用程序(浏览器、ping、curl)并不直接查询 DNS 服务器,而是调用 glibc 提供的解析函数(如 getaddrinfo())。
B. glibc 解析器(Stub Resolver):它的行为取决于 /etc/nsswitch.conf 的配置链:
- 现代 nss-resolve 路径:glibc 通过 IPC 直接与 systemd-resolved 通信,完全不经过 libc 默认的 UDP stub 路径。
- 传统 nss-dns 路径:它读取 /etc/resolv.conf 中指定的 nameserver(通常是 127.0.0.53)并发送 UDP 请求。
- 注意:非 glibc 程序(如 Go 的纯 Go 解析器)仍可能绕过此逻辑直接读取 /etc/resolv.conf。
C. 本地解析服务(systemd-resolved):接收到请求后,根据 per-link DNS 配置和路由策略选择上游 DNS。
D. 执行层(上游 DNS):最终将请求发往真正的 ISP、公共 DNS 或内网 DNS。

 

1. 为什么 resolv.conf 设计成“瞬态报告”?

这不是反人类,而是为了移动化适配。在 Wi-Fi、热点、VPN 频繁切换的今天,系统需要一个“调解员”实时处理链路冲突。如果 resolv.conf 是死文件,当你从公司 VPN 切换到手机热点时,由于 DNS 无法自动更新,你的网络会立刻瘫痪。

 

2. 权力架构:谁才是真正的老板?

在采用 systemd 体系的发行版中,DNS 解析通常由 systemd-resolved 负责聚合和管理。其生成的解析配置(位于 /run/systemd/resolve/ 下),通常由安装脚本或管理员手动通过符号链接映射到 /etc/resolv.conf核心契约:当该链接指向 systemd-resolved 时,网络管理器不再直接写入该文件,而是通过 D-Bus 将配置提交给它。

 

3. 核心痛点:DHCP “全家桶”污染

当你连上公共 Wi-Fi,DHCP 协议会强推一个 “全家桶”给你:包含 IP、网关和运营商提供的默认 DNS。更阴险的是 IPv6 后门:很多人只堵住了 IPv4,结果系统又通过 DHCPv6 或是 IPv6 的路由器广播(RA)偷偷塞进一个 IPv6 DNS 地址,导致解析再次被覆盖。

 

4. 三大对策、各自缺点及 VPN 影响 

正统派:经理人覆盖 (Overrides)

Ubuntu Server(使用 systemd-networkd 后端)可以在 Netplan YAML 中将 dhcp4-overrides 和 dhcp6-overrides 的 use-dns 设为 false。
⚠️ 生产环境警告:如果你使用的是 NetworkManager 后端,Netplan 的配置不会生效。你必须通过 nmcli 显式关闭自动 DNS 获取。如果你通过 SSH 远程操作,建议使用链式执行以缩短操作窗口,减少因连接中断导致的配置不一致风险:
# 链式执行:修改并立即激活。注意:&& 不能防御错误的配置,如果配置本身有误,你仍会失联。 nmcli con modify <connection-name> ipv4.ignore-auto-dns yes ipv6.ignore-auto-dns yes && nmcli con up <connection-name>
专家建议:对于极其关键的远程服务器,最稳妥的做法是在执行前预设一个 5 分钟后的自动回滚任务(如 echo "nmcli con up <old-connection-name>" | at now + 5 min),确保即使配置错误也能自动恢复连接。 


挂钩派:resolvconf 前置挂钩 (Prepending Hook)

通过修改 /etc/resolvconf/resolv.conf.d/head 文件,将配置强行拼接到生成的 resolv.conf 最顶部。
  • 实施前提:在使用 systemd-resolved 的系统上,通常需要将 /etc/resolv.conf 的控制权平滑移交给 resolvconf(如 openresolv 替代包),必要时需调整服务状态以避免接管冲突。

  • “咖啡店陷阱”:在需要网页认证的公共 Wi-Fi 下,除了传统的 DNS 劫持,部分现代网络已开始通过 DHCP/RA 下发认证 API(RFC 8910/8952)。强行固定 DNS 会破坏网络上下文感知与 Portal 发现流程,极易导致认证页面无法弹出。

  • VPN 干扰者:极易干扰依赖 resolvconfsystemd-resolved 动态注入 DNS 的 VPN(如 WireGuard 的 wg-quick 或 Tailscale)。写在 head 里的死配置会固化解析路径,极易压制或破坏 VPN 的 Split-DNS(分流解析)机制。(注:在 NSS 解析链中,收到 NXDOMAIN 会直接阻断查询;若遇到超时或 SERVFAIL,则可能触发不可预期的回退行为。)

破局之法:如果你的挂钩目标是 127.0.0.1 (dnscrypt-proxy),这并非死胡同,而是一个适合实现精细化分流的起点。不要解除 head 的锁死,而是将 OS 层的“配置冲突”转化为代理层的“智能分流”:

  1. dnscrypt-proxy.toml 中启用:forwarding_rules = 'forwarding-rules.txt'

  2. 在该 .txt 文件中按 <domain> <server address> 格式写入规则:corp 10.8.0.1corp $DHCP

(架构师防坑警告:官方声明 $DHCP 转发为实验性功能。面对 WireGuard/Tailscale 等非 DHCP 协议下发的 tun 隧道时,它未必能可靠覆盖此类 VPN 场景。针对动态分配的内网 DNS,在服务端固定网关 IP,或在客户端编写 NetworkManager dispatcher.d 钩子脚本进行实时同步,是实战中更为稳妥的工程解法。)

暴力派:chattr 焊死 (The Nuclear Option)

删掉软链接改实体文件,写死 8.8.8.8 后执行 chattr +i /etc/resolv.conf
  • 排障噩梦:NetworkManager 会将其标记为 unmanaged;而 systemd-resolved 会进入“消费者模式”(Consumer Mode),仅仅将其作为向下兼容的参考输入。
  • 分歧路径:绕过 libc 直接读文件的程序(如 Go/Node.js),其最终查询目标将不可避免地退化为你锁死的 8.8.8.8,导致 VPN 的 per-link DNS 路由报废。

 

5. 那个消失的 127.0.0.53

之所以称之为“消失”,是因为在现代 Linux 配置下,你能在 /etc/resolv.conf 中直视这个地址,却无法在任何物理网络接口上捕获它的流量。虽然 netstat 显示 127.0.0.53 处于监听状态,但由于 nss-resolve 优先走内存级 IPC 通信,绝大多数解析流量根本不会经过 lo 回环接口。
  • 流量路径
  • 前半程 (App → Resolver):如果是现代 App,走 IPC 路径,lo 接口上抓不到包;如果是传统 App(如 dig),走 UDP 路径,你在 lo 上能抓到包。
  • 后半程 (Resolver → 上游):一旦本地缓存不命中,systemd-resolved 必须向外求助。此时报文会通过路由表从物理网卡(如 wlo1)发出。
实际上,systemd 官方强烈建议程序通过 nss-resolve 直接通信。而 127.0.0.53 仅仅是为那些直接发送 UDP 裸请求的程序提供的兼容性 Stub Listener(官方从 v252 开始还在 127.0.0.54 提供了一个跳过本地合成逻辑、偏向纯转发语义的 Proxy 模式)。

 

6. 真正的入口:nsswitch.conf

在 Linux 中,解析顺序取决于 /etc/nsswitch.confhosts: 行。现代发行版常见的配置如下: hosts: files resolve [!UNAVAIL=return] dns
  1. 顺序决定论:glibc 严格从左到右按序调用模块。尽管官方建议将 resolve 排在 files 前以利用缓存,但多数发行版仍优先保障 /etc/hosts 的权威。
  2. 断路器机制[!UNAVAIL=return] 意味着只有当 systemd-resolved 进程彻底死亡或不可达时,才会向右回退。如果只是单纯的域名不存在(NXDOMAIN),解析会直接终止,不会触发回退。

 

7. 短路逻辑与敏感泄露

在许多被旧版部署工具、自动化脚本或人工干预过的系统中,/etc/nsswitch.conf 往往会变成一个充满陷阱的乱序版本。问题并不在于解析模块本身有毒,而在于 NSS 的链式顺序与断路器语法被错误地拼接成了如下模样:
hosts: files mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] dns mymachines
 
第一道封锁线:.local 与断路器引发的解析车祸
nss-mdns 模块的职责本就是通过 mDNS 解析 .local,这无可厚非。但如果你公司的 AD 域为 corp.contoso.local,在上述带有断路器的顺序下将演变成灾难。mdns4_minimal 优先在物理局域网发送组播,找不到后,紧随其后的 [NOTFOUND=return] 指令会瞬间截断后续的查询链。这意味着你的 VPN DNS(即使配置在了 resolve 或 dns 层面)根本收不到请求,内网身份验证直接熔断。这是一种极其典型的配置顺序陷阱。 
 
队尾的刺客:乱序 mymachines 的隐私泄露
nss-mymachines 用于解析注册到本地 systemd-machined 的容器名。系统官方标准建议将其放置在 resolve 和 dns 之前,以保障本地容器名优先命中,这在正常配置的系统下是完全安全的。然而,在被篡改或错误配置的乱序系统中(如上述配置中它掉到了 dns 之后),它就成了“队尾的刺客”。如果你 ping 一个本地容器(如 test-db),系统会先让 dns 模块去查一圈公网报错后,才轮到它。这意味着这种配置级的 Bug,会将你的本地容器命名架构明文泄露给配置的上游 DNS 服务器(即便上游最终返回 NXDOMAIN,带有你容器名的查询包也已经出网被记录)

 

8. Netplan 的秩序 vs. 恶意的破坏: Netplan 的逻辑秩序与版本敏感性

许多人试图在 YAML 中配置 dhcp4-overrides: use-dns: false 来一劳永逸地拒绝 DHCP 污染。这确实确立了一种逻辑秩序,但请务必注意后端与版本的敏感性:Netplan 官方文档指出,use-dns 设定主要针对 networkd 后端生效。
尽管现代 Netplan 确实会尝试将 dhcp4-overrides 翻译并应用到 NetworkManager 的 DHCP 流程中(转化为 NM 的 ipv4.ignore-auto-dns=yes 属性),但这种跨后端的映射高度依赖版本,在诸如 Ubuntu 20.04 LTS 等仍被广泛使用的旧版系统中极易失效。因此,在极度关键的生产场景中,切勿盲目依赖 YAML 的抽象翻译,更稳妥的做法是直接核实 nmcli 的实际执行状态或底层生成的 keyfile
秩序防不住“破门者”
即使你完美锁定了底层配置,如果有流氓 VPN 客户端直接以 root 权限执行 write() 强覆写 /etc/resolv.conf,或者恶意进程强占了与 systemd-resolved 冲突的 53 端口(例如试图绑定 0.0.0.0:53 而非避开 127.0.0.53:53),你的 nslookup 依然会发生泄露或引发服务死锁。秩序约束的是遵循系统调用的合法组件;要防备真正的破坏者,你依然需要求助于 AppArmor 等强制访问控制或底层的包过滤机制。

9. 隐私防护:DoT、DoH 与 DNSSEC

纯靠锁死 8.8.8.8 无法防止基于明文 DNS(UDP/TCP 53)的链路劫持或透明代理。systemd-resolved 原生支持 DNS-over-TLS (DoT)(用于传输链路加密)以及 DNSSEC(用于记录完整性校验)。通过修改 /etc/systemd/resolved.conf 启用 DNSOverTLS=yes,在上游解析器支持 DoT 的前提下,让解析请求在 TLS 加密通道中传输。这才是比单纯修改文件更深层的防护机制(可防止链路监听与篡改,但不改变对上游解析器的信任假设)。(注:yes 为严格模式,上游不支持 DoT 时查询将直接失败而非回退明文。如需兼顾可用性,可先用 opportunistic 验证上游支持情况。)

DoH 流量混淆
TLS 的 853 端口特征明显,易遭识别阻断。dnscrypt-proxy 支持 DNS-over-HTTPS (DoH, Port 443)。DoH 流量与标准 HTTPS 融合,规避针对特定端口的 DPI 拦截。此场景下,systemd-resolved 仅作为调度存根(Stub),加密与混淆由代理层执行。


10. DNS 代理的乱入与内核死锁陷阱

当你试图通过 /head 引入本地代理时,必须小心 53 端口的启动死锁。默认情况下,systemd-resolved 牢牢绑定在 127.0.0.53:53。虽然这与 127.0.0.1 是独立的 IP,但部分传统的第三方缓存工具(如 dnsmasq)在出厂配置下会默认尝试全局监听(绑定 0.0.0.0:53)。在 Linux 内核机制下,当具体 IP 被占用时,后续的全局通配符绑定(Wildcard Bind)会被内核直接以 Address already in use 拒绝。这就是为什么许多极客配置了本地代理却莫名启动失败——除非你懂得去深入修改代理工具的底层配置(如在 dnsmasq 中强制开启 bind-interfaces),否则最终往往不得不退回硬编码 8.8.8.8

架构师的解法 (The Socket Bypass):dnscrypt-proxy 这类强制显式声明监听地址的工具,本身就能避开 0.0.0.0 通配符死锁。而更进阶的系统级解法,是直接利用 systemd 的 Socket Activation(套接字激活)。
无需解除 head 的锁死,配置 .socket 单元,将 127.0.0.1:53 的监听权交由 PID 1 接管。这种基于精确匹配的端点控制能确保代理程序优先拿到端口,彻底消除启动时序的竞争。
(注:此架构生效的底层前提是代理工具必须实现了 sd_listen_fds() 接口来接收系统传递的描述符。dnscrypt-proxy 原生具备此能力,从而完美达成了与 systemd-resolved 的无缝共存。)

11. 软链接的“狸猫换太子”

在 Ubuntu 中,/etc/resolv.conf 的指向决定了你的“抗干扰能力”:
  • 模式 A(默认):指向 ../run/systemd/resolve/stub-resolv.conf。解析经过管理员过滤,支持 Split-DNS。
  • 模式 B(底层):指向 ../run/systemd/resolve/resolv.conf。上游 DNS 直接暴露在文件里,不经过 127.0.0.53 转发。

 

12. 让理论成为可见的数据

  • 查看当前真实使用的 DNSresolvectl status
  • 定位 Split-DNS 链路污染的神器resolvectl query google.com
    (它会绕过 glibc 告诉你最终选用了哪个网卡接口和哪个具体服务器)
  • IPC 观测法: resolvectl monitor 。你可以使用此命令实时监听 D-Bus 总线上的解析请求。法证级盲点警告:它只能看到经过 systemd-resolved 处理的查询。如果你的程序完全绕过 systemd-resolved、直接向外部 DNS 发包(例如 resolv.conf 被锁死为外部地址,或程序内置了硬编码 DNS),monitor 的界面上将是一片死寂。这种"看不见",正是底层规则被绕过的最强铁证。

 

13. 观测与旧时代的遗产:nscd 导致的缓存缺失

古代 Linux 依靠 nscd 在 libc 层面拦截并提供独立缓存。现代发行版已默认不再预装该组件,并在很大程度上将其边缘化(尽管部分 LDAP/NIS 企业环境仍在苟延残喘)。因此,只要你的程序(如纯 Go 解析器)或挂钩策略绕过了 systemd-resolved 的 IPC 接口,你的查询就会走传统的 nss-dns 路径。由于该路径默认不带缓存,你将跌落回“零缓存”状态,每次查询都会触发真实的物理发包。

14. 服务器环境下的生死法则

  • 云厂商的 VPC 结界:云架构完全依赖云厂商提供的“内网专属 DNS”(如 AWS 的 169.254.169.253)。在云上,必须优先使用云内网 DNS,否则会导致内网服务解析熔断。
  • 代理中转税:在极高并发场景下(如单机数万 QPS),本地 stub 转发路径会引入额外的内核态与用户态切换(Context Switch)及潜在的锁竞争。

 

15. Docker/K8s 的云原生结界

为什么运行在系统上的容器不会被宿主机的 127.0.0.53 搞崩溃?因为容器引擎拥有一套独立且强悍的旁路法则:
  • Docker 的条件触发回退(Conditional Fallback):在默认的 bridge 网络中,Docker 会参考并继承宿主机的 /etc/resolv.conf(并经过引擎的过滤与重写)法证级细节:如果 Docker(底层 libnetwork 源码)发现宿主机的配置中包含回环地址(如 127.0.0.53),为了防止容器内发生不可逾越的路由死循环,会强制将其剔除。更暗黑的是,如果剔除后没有留下任何有效的上游 DNS,在部分实现与运行环境中,可能会触发兜底机制,注入预设的公共 DNS 服务器(如 8.8.8.88.8.4.4)。而在自定义网络(User-defined networks)中,Docker 通常会启用内嵌的 DNS 引擎(固定为 127.0.0.11)(除非被显式 DNS 参数或特殊网络模式如 host 覆盖),负责容器内的服务发现与上游转发。
  • K8s 的集群级接管:在 Kubernetes 世界里,DNS 解析被彻底拔高到了集群控制面。kubelet 会根据 dnsPolicy 策略接管并重写容器内的 /etc/resolv.conf 的内容。在默认的 ClusterFirst 策略族(及相关变体)下,这意味着容器的解析路径不再依赖宿主机的 systemd-resolved 守护进程或其宿主级 NSS 解析链,而是通过容器内部的 libc Resolver 指向集群 DNS Service(通常为 CoreDNS 的 ClusterIP,底层经由 kube-proxy 或 IPVS 完成 VIP 流量转发),将解析路径控制权完全收敛至集群控制面(对于集群外部域名,解析请求最终仍可能由 CoreDNS 根据配置转发至外部上游)。(注意:如果你手贱设置了 dnsPolicy: Default,Pod 将直接继承宿主机的解析配置,从而可能再次暴露于宿主机解析机制引发的异常,例如回环地址不可达)。

 

16. Android 与系统的分化: 移动端的终极形态(DnsResolver APEX)

在 Android 系统中,/etc/resolv.conf 早就被抛弃。在 Android 9 及以前,解析从 Bionic libc 走向 netd 的 Socket;而从 Android 10 开始,为了追求更独立的模块化升级,DNS Resolver 被独立为 com.android.resolv APEX 模块。现代 Android 架构通过底层专有 API 与这个独立的系统模块进行高性能 IPC 通信。这印证了现代操作系统在网络切换频繁的场景下,“废弃静态文件,拥抱模块化进程通信”是不可逆的演进方向。

Friday, 20 December 2024

LinkedIn - Ellipsis Bug

Firefox users might have recently noticed that LinkedIn profiles display an ellipsis ("…") but do not allow the text to expand when clicked.

, whereas Chrome users encounter no such issue.


Firefox: About section


Firefox: Experience section


Chrome: About section


Chrome: Experience section


Example of Minimal Code:

<html lang="en">
<head>
    <style>
        .ZPcJ {
            position: relative;
        }

        .inline-show-more-text--is-collapsed-with-line-clamp {
            display: -webkit-box;
            -webkit-line-clamp: 3;
            -webkit-box-orient: vertical;
        }

        .inline-show-more-text__link-container-collapsed {
            position: absolute;
            bottom: 0;
            right: 0;
            margin: 0;
            padding: 0 0 0 0.4rem;
        }
    </style>
</head>
<body>
    <div class="ZPcJ">
        <div class="inline-show-more-text--is-collapsed-with-line-clamp">
            Proficient Equipped with hands-on experience gained through managing diverse projects during my professional and academic journey, I bring a harmonious balance of analytical thinking, creativity, and resourcefulness to the table.
            Motivated by a passion for innovation, I approach every endeavor with determination and a focus on achieving impactful results. From designing scalable architectures to optimizing workflows, I continuously seek opportunities to refine my skills and explore uncharted solutions.
            <button type="button" class="inline-show-more-text__link-container-collapsed">
                …see more
            </button>
        </div>
    </div>
</body>
</html>

Output:

Chrome always shows the button

Firefox


When the "…see more" button is wrapped inside the same div as the clamped text, the button becomes overlaid and inaccessible.

Chrome is able to display the button, while Firefox does not render it.

A Firefox developer claimed it's a bug in LinkedIn because not rendering the element is the correct behavior per the specifications:



Firefox Nightly 135 has already implemented a short-term fix:


Part of the code fix rolls back the pre-21 Oct behavior by adding a pref:


Saturday, 16 September 2023

Building and Running the Linux Kernel Using Qemu: In a Nutshell

xb@dnxb:~$ sudo apt update
# Install the necessary packages for building the Linux kernel:
xb@dnxb:~$ sudo apt-get install build-essential libncurses5-dev bison flex libssl-dev libelf-dev
# libvirt-bin is now split into libvirt-daemon-system and libvirt-clients.
xb@dnxb:~$ sudo apt install qemu-kvm qemu virt-manager virt-viewer libvirt-daemon-system libvirt-clients

# To resolve the error "pahole (pahole) is not available" while making the kernel.
xb@dnxb:~$ sudo apt install dwarves
# Test in Ubuntu, not using Linux source at github
xb@dnxb:~$ sudo apt install linux-source
# After the installation, the source is typically located in `/usr/src/`
xb@dnxb:~$ cd /usr/src/
# You'll need to modify the subsequent commands "...5.4.0..." based on the latest version.
xb@dnxb:/usr/src$ cd linux-source-5.4.0
xb@dnxb:/usr/src/linux-source-5.4.0$ cp linux-source-5.4.0.tar.bz2 ~/Downloads/
xb@dnxb:/usr/src/linux-source-5.4.0$ cd ~/Downloads/

# OR use `pv linux-source-5.4.0.tar.bz2 | tar -xjf-` to monitor the extraction progress.
xb@dnxb:~/Downloads$ tar xjf linux-source-5.4.0.tar.bz2
# ... If you use Nautilus for extraction, you need to copy it from /usr/src/ to a regular user path before extracting.
# ... However, when using Nautilus, you might need to eliminate any redundant directories that were created by mistake. Eliminate them early, as changes in directory timestamps seem to slow down the remaking process.
# e.g. xb@dnxb:~/Downloads/redundant_folder/linux-source-5.4.0$

# To fix `No rule to make target 'debian/canonical-certs.pem', needed by 'certs/x509_certificate_list'. Stop.` when make kernel from Ubuntu because the .config file contains "debian/" certs:
xb@dnxb:~/Downloads/linux-source-5.4.0$ cp -r /usr/src/linux-source-5.4.0/debian ./
xb@dnxb:~/Downloads/linux-source-5.4.0$ cp -r /usr/src/linux-source-5.4.0/debian.master ./

# As an initial modification trial, add "hello world" after `pr_notice("%s", linux_banner);`:
xb@dnxb:~/Downloads/linux-source-5.4.0$ grep -i hello init/main.c -C 2
page_address_init();
pr_notice("%s", linux_banner);
pr_notice("Hello, World! from the Kernel by hole\n");
early_security_init();
setup_arch(&command_line);
xb@dnxb:~/Downloads/linux-source-5.4.0$

# ARCH=x86_64 is optional, while the default ARCH=x86 lets you toggle off the 64-bit kernel in the menu.
# Just click <Exit> then [Save]. If re-edit main.c to add print, no need run this:
xb@dnxb:~/Downloads/linux-source-5.4.0$ make menuconfig ARCH=x86_64
xb@dnxb:~/Downloads/linux-source-5.4.0$ make -j$(nproc) ARCH=x86_64
# If you re-edit main.c to add a print, you don't need to run `make modules`, just `make -j$(nproc) ARCH=x86_64 && make bzImage -j$(nproc) ARCH=x86_64`
# "Section mismatch" warnings can be safely ignored.
# Can type '?' to see details if seeing "[N/m/y/?] (NEW) ?", simply "y"
xb@dnxb:~/Downloads/linux-source-5.4.0$ make modules -j$(nproc) ARCH=x86_64
xb@dnxb:~/Downloads/linux-source-5.4.0$ make bzImage -j$(nproc) ARCH=x86_64

# Despite the ARCH=x86_64 setting, the kernel image path is ./arch/x86/boot/bzImage.
# The reason is the unified source code for x86 and x86_64 in the Linux kernel.
# x86 covering both x86 & x86_64 through scripts/subarch.include and .config .
# The x86_64 path is simply a symbolic link to x86.
# Use the x86 path to qemu as it's the real location for the kernel image.
xb@dnxb:~/Downloads/linux-source-5.4.0$ file ./arch/x86_64/boot/bzImage
./arch/x86_64/boot/bzImage: symbolic link to ../../x86/boot/bzImage
xb@dnxb:~/Downloads/linux-source-5.4.0$ realpath ./arch/x86/boot/bzImage
/home/xiaobai/Downloads/linux-source-5.4.0/arch/x86/boot/bzImage
xb@dnxb:~/Downloads/linux-source-5.4.0$

# Create an Initramfs:
# For a simple test, you can create a very basic initramfs that only has a /init script. Here's an example:
# replace 'hole' with your preferred directory name
xb@dnxb:~/Downloads/linux-source-5.4.0$ mkdir hole; cd hole/
xb@dnxb:~/Downloads/linux-source-5.4.0/hole$ mkdir -p myinitramfs
xb@dnxb:~/Downloads/linux-source-5.4.0/hole$ cd myinitramfs/
xb@dnxb:~/Downloads/linux-source-5.4.0/hole/myinitramfs$ mkdir -p bin sbin etc tmp proc sys usr/bin usr/sbin
xb@dnxb:~/Downloads/linux-source-5.4.0/hole/myinitramfs$ echo -e '#!/bin/sh\nmount -t proc none /proc\nmount -t sysfs none /sys\nexec /bin/sh' > init
xb@dnxb:~/Downloads/linux-source-5.4.0/hole/myinitramfs$ chmod +x init
xb@dnxb:~/Downloads/linux-source-5.4.0/hole/myinitramfs$ cd ..

# You can visit https://busybox.net/downloads/ to get the latest version. You'll need to modify the subsequent commands "...1.36.1..." based on the latest version:
xb@dnxb:~/Downloads/linux-source-5.4.0/hole$ wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
xb@dnxb:~/Downloads/linux-source-5.4.0/hole$ tar xjf busybox-1.36.1.tar.bz2
xb@dnxb:~/Downloads/linux-source-5.4.0/hole$ cd busybox-1.36.1/
# Load the default configuration:
xb@dnxb:~/Downloads/linux-source-5.4.0/hole/busybox-1.36.1$ make defconfig
# "Settings" -> "Build Options" -> "Build static binary (no shared libs)" and press 'Y' otherwise will get "Kernel panic - not syncing: No working init found." after boot kernel.
xb@dnxb:~/Downloads/linux-source-5.4.0/hole/busybox-1.36.1$ make menuconfig
xb@dnxb:~/Downloads/linux-source-5.4.0/hole/busybox-1.36.1$ make -j$(nproc)

# Now, create the initramfs archive. CPIO is the preferred method because it effectively duplicates a file system, backs it up, and preserves unique file features like hardlinks and FIFOs:
xb@dnxb:~/Downloads/linux-source-5.4.0/hole/busybox-1.36.1$ cd ../myinitramfs
# Use BusyBox as the shell program for initramfs:
xb@dnxb:~/Downloads/linux-source-5.4.0/hole/myinitramfs$ cp ../busybox-1.36.1/busybox bin/
xb@dnxb:~/Downloads/linux-source-5.4.0/hole/myinitramfs$ cd bin/
xb@dnxb:~/Downloads/linux-source-5.4.0/hole/myinitramfs/bin$ ln -s busybox sh
xb@dnxb:~/Downloads/linux-source-5.4.0/hole/myinitramfs/bin$ ln -s busybox mount
xb@dnxb:~/Downloads/linux-source-5.4.0/hole/myinitramfs/bin$ cd ..
xb@dnxb:~/Downloads/linux-source-5.4.0/hole/myinitramfs$ find . | cpio -H newc -o | gzip > ../myinitramfs.cpio.gz
xb@dnxb:~/Downloads/linux-source-5.4.0/hole/myinitramfs$ cd ../..

# Boot the kernel with qemu. You might need to press Enter to access the shell. To exit, use "Ctrl+a", release it, then press "x". If there's an issue, use killall qemu-system-x86_64.
# append "panic=10" will reboot 10 seconds after kernel panic
xb@dnxb:~/Downloads/linux-source-5.4.0$ qemu-system-x86_64 -kernel /home/xiaobai/Downloads/linux-source-5.4.0/arch/x86/boot/bzImage -initrd /home/xiaobai/Downloads/linux-source-5.4.0/myinitramfs.cpio.gz -append "console=ttyS0 earlyprintk=serial,ttyS0,115200 debug" -nographic
...




Wednesday, 17 May 2023

Is TikTok listening to my conversation?

My friend invited me to lunch and mentioned the word "cafe", but I didn't go.

After 4 hours, I noticed a post related to cafes in my Facebook news feed:


This post isn't very popular and I haven't heard the word "Cafe" for a long time, I can't even recall the last time. So I realized this post is not a coincidence, not again (something I type in ChatGPT also shows similar ads, either OpenAI or my keyboard is selling my data, as expected~).

So I quickly dumped adb logcat, hoping the relevant logs hadn't disappeared yet. Fortunately, it did retain the 05-16 19:39:17.603 log from the previous night:



The next thing I found is quite intriguing:


I recall my friend speaking to me around 12:30 pm (not sure of the exact time, but it was before 1 p.m.), and coincidentally, a suspicious log appears here that is too blatant to ignore!

The com.vivo.smartshot is a system app on my Vivo phone. I extracted the APK using that package name and found its app label is 'S-Capture' (gi is grep -i):


And yes, it has the microphone/record audio permission:


When I go to the system app settings, its permissions can't be turned off since it's a "system app"!



What is the "S-capture" app really? The Vivo site shows:



I'm only able to take a screenshot by swiping down with three fingers, but I don't see the floating dialogue for recording. It might be a different version, but I don't see any log showing SmartShot.

CVE of Vivo smartshot:


And the Calling a method in the system process without a qualified user is just a warning, not an indication of operation failure, as shown in AOSP:





I initially suspected that S-Capture was the problematic app, or that another app was exploiting the CVE associated with it.

Tuesday, 14 March 2023

ChatGPT is not the AI you expected

ChatGPT has explained why a non-existent book was listed in its response. 


This is similar to a human creating a dream that may not necessarily exist in the physical world. 
 
Dream is useful for creativity but you should not expect ChatGPT to always provide you with factual information.



Saturday, 6 August 2022

Chat bot 不是这样用的 - Celcom














Celcom Customer Service Messenger/Whatsapp chat robot:

1. 可以用 Live Chat Agent, AGENT, agent, Cool Agent, hi agent me 进入跟真人询问的环节,稍微复杂的就不行了。除了 Agent ,其它都是 I didn't quite understand/get, a bit advanced to me, Hmm..。

2. 五个月前 Live Chat Agent 无效应该是 fixed 了。

3. 输入 Support Inquiry/Case 后输入非号码的文字可以触发 invalid 后的 Live Chat Agent / AGENT 菜单建议。其他如 Account Status 选项出现就不能输入 Support Inquiry,必须等 OTP 失败/EXIT 后才能。

4. 不输入超过 256 字。输入 No 退出。


多少人被拒之门外 😶:
- https://www.facebook.com/groups/mobilemamak/posts/584040922949692/
- https://www.facebook.com/donat.darkhannaz/posts/pfbid02yiBUgta2DdLqZRcyqfEKdEmEczvSej1SLmVhDYoR9zXha7Nk1uzgRG1mhqpXmmW5l
- https://www.facebook.com/photo?fbid=5339132769450317
- https://www.facebook.com/zainal.ariffin.5/posts/pfbid034voWSUwWjGYQXDwbmAtSS5qCsNs4zo8TPyVK1rnbsJrn52itxTFjyaxMKwqSUdxvl

客服是省人工钱 + 避免小孩子乱玩,但是把 bug/投诉 蒙上双眼就 No case 真的不敢苟同。

之所以不拨打 1111 是因为曾一度(现在好像有了)没有 loss phone 选项, 只给你听广告。

另外吐槽一下,都 loss phone 了紧急 de-activate simcard 了还要浪费时间听 "且听我一个字一个字慢慢道来的广告"。

有些(旧?)手机很不友好,还要提前按 press keypad,否则真的摸不着要按哪里。 然后?重新听广告。

Chat bot 也有广告,用户有问题了已经很恼怒还要在迷宫般的 chatbot 重复看广告真的是花惹发。

淘宝也有类似问题,我都回复了几次大马客服手机打不进,却无限循环重复给大马客服手机号,绕了大圈子才升级到较好的人工客服。由于是 bug 客服也不是技术人员,最终仍然解决不了,但起码我得知手机 app 才能登陆到有问题的手机账号。

prestomall 也是花了一阵子才懂,需要手机 app 才能做到想要的换手机号功能。甭管淘宝或 prestomall, 网页版都不被重视是 buggy 的。