从一粒沙子到人工智能:计算机到底是怎么工作的

如果用一句话概括计算机的全部原理,那就是:

用电的开和关,模拟逻辑的真和假。

听起来很简单,甚至有点粗糙。但现代计算机、操作系统、编译器、虚拟机,甚至今天的人工智能,本质上都是从这个简单事实一层一层堆出来的。

计算机世界最神奇的地方不在于它一开始就很复杂,而在于它用极其简单的规则,构造出了极其复杂的系统。

一、最底层:电的开和关

想象你面前有一根电线。

通电,我们记作 1

断电,我们记作 0

这就是计算机里最小的信息单位:bit,比特

一个 bit 本身几乎什么也做不了。它就像一个灯泡,只能亮或者灭。

但是,如果我们把很多个 bit 组合起来,事情就开始变得有意思了。

比如:

1
2
0
1

可以表示真假。

1
2
3
4
00
01
10
11

两个 bit 可以表示四种状态。

8 个 bit 可以表示 256 种状态,也就是一个字节。

于是,数字、字母、图片、声音、视频,最终都可以被编码成一串串 01

但光有 01 还不够。计算机不只是存储信息,它还要处理信息。

那么,处理信息靠什么?

靠逻辑门。

二、逻辑门:用电路表达逻辑

假设有一个电路,它有两个输入,一个输出。

它的规则是:

只有两个输入都通电时,输出才通电。

这就是 与门

用逻辑表示就是:

1
2
3
4
1 AND 1 = 1
1 AND 0 = 0
0 AND 1 = 0
0 AND 0 = 0

类似的基本逻辑门还有:

  • 或门:只要有一个输入是 1,输出就是 1
  • 非门:输入是 1,输出就是 0;输入是 0,输出就是 1
  • 异或门:两个输入不同,输出为 1;相同则为 0

这些门看起来很简单,但它们是计算机世界的积木。

只要把这些逻辑门组合起来,就可以构造出更复杂的电路。

其中最关键的一种电路,叫做 加法器

三、加法器:计算能力的起点

二进制加法的规则很简单:

1
2
3
4
0 + 0 = 0
0 + 1 = 1
1 + 0 = 1
1 + 1 = 10

最后一个 1 + 1 = 10 的意思是:

当前位结果是 0,向高位进 1

这个过程可以用逻辑门实现。

比如:

  • 当前位的结果,可以用异或门得到
  • 是否产生进位,可以用与门得到

也就是说,我们可以用电路搭出一个真正能做加法的机器。

一旦有了加法,很多事情就可以继续推导出来:

  • 减法可以看作加负数
  • 乘法可以看作反复加法
  • 除法可以看作反复减法
  • 比较大小可以通过减法判断结果
  • 位运算可以直接通过逻辑门完成

所以,从某种意义上说:

有了加法,计算机就拥有了计算的基础。

但此时我们得到的还只是一个计算器。

计算器和计算机之间,还差一个关键能力。

四、计算器和计算机的区别

计算器需要人来按按钮。

你按 1 + 1 =,它才计算 1 + 1

你按 5 × 8 =,它才计算 5 × 8

也就是说,计算器本身不会决定下一步该做什么。

而计算机不同。

计算机可以自己从内存里读取指令,然后自己执行。

这就是计算机真正强大的地方。

它不只是能计算,它还能自动执行一系列计算步骤。

这个过程可以概括为三个动作:

1
取指令 → 解码 → 执行

CPU 一生都在做这件事。

每秒几十亿次。

五、CPU 到底在干什么?

CPU 可以粗略分成几个部分:

  • 控制单元
  • 算术逻辑单元
  • 寄存器
  • 缓存
  • 总线接口

其中最核心的是两个部分:

1. 算术逻辑单元

算术逻辑单元,也就是 ALU,负责真正的计算。

比如:

  • 加法
  • 减法
  • 与运算
  • 或运算
  • 异或运算
  • 比较大小

它就是前面那些逻辑门和加法器的高级组合。

2. 控制单元

控制单元负责指挥。

它会从内存里读取一条指令,然后判断这条指令是什么意思。

比如某条指令可能表示:

1
2
3
4
把内存 A 里的数字取出来
把内存 B 里的数字取出来
相加
把结果存回内存 C

CPU 不断重复这个过程:

1
2
3
4
读取指令
理解指令
执行指令
读取下一条指令

这就是所谓的 取指-译码-执行循环

计算机所有复杂行为,本质上都是这个循环高速重复的结果。

六、内存:存放数据,也存放指令

现在问题来了。

CPU 从哪里读取指令?

答案是:内存。

内存可以理解为一大片格子。

每个格子里都存着一串 01

这些 01 可以是数字,可以是文字,可以是图片,也可以是 CPU 要执行的指令。

这里出现了计算机历史上极其重要的一点:

指令和数据,在内存里长得一模一样。

它们都是二进制。

比如:

1
01011001

这串东西到底是一个数字,还是一个字符,还是一条 CPU 指令?

它自己并不知道。

关键在于:CPU 如何解释它。

如果 CPU 把它当成数字,它就是数字。

如果 CPU 把它当成指令,它就是指令。

这就是冯·诺依曼架构最重要的思想之一:

程序和数据统一存储。

七、冯·诺依曼架构:现代计算机的灵魂

在冯·诺依曼架构里,程序和数据都放在内存中。

CPU 按照程序计数器指向的位置,一条一条读取指令并执行。

这带来了一个非常深远的结果:

程序可以处理程序。

一段程序可以读取另一段程序。

一段程序可以生成另一段程序。

一段程序甚至可以修改自己。

这意味着计算机不再是一台固定功能的机器。

它变成了一台可以通过更换程序来改变用途的通用机器。

同一台电脑:

  • 装上浏览器,它就能上网
  • 装上游戏,它就能娱乐
  • 装上编译器,它就能写程序
  • 装上虚拟机,它就能模拟另一台计算机
  • 装上操作系统,它就能管理其他程序

硬件没有改变。

改变的只是内存里那一串串 01

这就是现代计算机最根本的力量。

八、编译器:程序生成程序

既然程序本身也是数据,那么我们就可以写一个程序,让它去处理另一段程序。

比如,人类写的代码是这样的:

1
int a = 1 + 2;

CPU 并不能直接理解这句话。

CPU 只能理解机器指令,也就是二进制。

于是我们需要一个程序,把人类能读懂的代码翻译成 CPU 能执行的机器码。

这个程序就是 编译器

编译器本质上也是一个普通程序。

它读取文本,分析语法,理解结构,然后生成另一批二进制指令。

也就是说:

编译器是一个生产程序的程序。

这件事非常重要。

因为有了编译器,人类就不需要直接面对 01 了。

我们可以写 C、C++、Java、Go、Rust、Python,再由编译器或解释器转换成机器能够执行的形式。

软件文明由此开始膨胀。

九、操作系统:管理所有程序的程序

如果计算机只能一次运行一个程序,那还不够好用。

我们需要同时打开浏览器、编辑器、音乐播放器、终端和聊天软件。

问题是:

CPU 到底先执行谁?

内存怎么分配?

硬盘谁来读写?

键盘输入给哪个程序?

网络数据交给谁?

这些事情都需要一个总管。

这个总管就是 操作系统

操作系统也是程序。

只不过它是一个特殊的程序,负责管理其他程序。

它主要做几件事:

  • 管理 CPU 时间
  • 管理内存
  • 管理文件系统
  • 管理输入输出设备
  • 管理进程和线程
  • 提供系统调用接口

应用程序不能直接随便操作硬件。

它们需要请求操作系统,由操作系统统一调度。

所以,我们平时写程序,很多时候并不是在直接控制硬件,而是在调用操作系统提供的能力。

比如读文件、开网络连接、创建进程,本质上都是在请求操作系统帮忙。

十、虚拟机:用程序模拟另一台计算机

既然程序可以处理程序,那还能做一件更离谱的事:

写一个程序,假装自己是一台计算机。

这就是虚拟机。

虚拟机可以模拟 CPU、内存、硬盘、网卡等硬件设备。

真实硬件上运行着一个操作系统。

操作系统里运行着一个虚拟机程序。

虚拟机程序里面又运行着另一个操作系统。

这听起来像套娃,但原理仍然没有变:

CPU 执行程序。

程序模拟硬件。

被模拟出来的硬件继续执行另一个程序。

这就是为什么我们可以在一台电脑上运行多个系统。

也是为什么云计算可以把一台大型服务器切分成很多台虚拟服务器。

十一、从晶体管到软件文明

现在我们把链路重新串起来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
电的开关

0 和 1

逻辑门

加法器

算术逻辑单元

CPU

内存

取指令、解码、执行

程序和数据统一存储

编译器

操作系统

虚拟机

现代软件生态

看起来复杂,但每一层都是建立在上一层之上的。

没有魔法。

只有抽象。

所谓计算机科学,很大程度上就是不断把底层复杂性封装起来,然后在更高一层继续构建新的系统。

底层是电路。

上一层是指令。

再上一层是汇编。

再上一层是高级语言。

再上一层是操作系统 API。

再上一层是框架、应用、服务和人工智能。

每一层都隐藏了下面的细节,让人类可以处理更复杂的问题。

十二、人工智能也是这条链路的延续

最后再看人工智能。

人工智能看起来像是另一个世界。

模型、神经网络、参数、训练、推理、涌现能力,这些概念听起来和逻辑门、CPU、内存相距很远。

但从本质上说,AI 仍然运行在同一套计算机体系之上。

神经网络的核心运算是什么?

大量矩阵乘法。

矩阵乘法又是什么?

大量乘法和加法。

乘法和加法又是什么?

逻辑门组合出来的电路操作。

所以,即使是今天的大语言模型,归根结底也没有脱离那条链路:

1
晶体管 → 逻辑门 → 加法器 → CPU/GPU → 矩阵运算 → 神经网络 → 大语言模型

AI 的特殊之处在于,它不是靠人类手写规则来完成任务,而是通过大量数据和计算,调整模型内部的参数。

以前的很多人工智能系统依赖人工规则。

人告诉机器:

1
2
如果出现 A,就执行 B
如果出现 C,就执行 D

但现代神经网络不是这样。

它更像是通过海量样本自己学出一种复杂的映射关系。

你给它输入,它给你输出。

中间的规律不是人类逐条写进去的,而是在训练过程中逐渐形成的。

十三、为什么算力改变了一切?

神经网络并不是最近才出现的东西。

它的思想已经存在了很久。

但早期神经网络并不强,因为它需要巨大的数据和算力。

过去的计算机不够快,数据也不够多。

所以很长一段时间里,基于规则的方法更受欢迎。

后来事情变了。

CPU 越来越快。

GPU 被用于通用计算。

互联网积累了海量数据。

分布式计算和云计算降低了训练大模型的门槛。

于是神经网络终于等到了适合自己的时代。

尤其是 GPU,非常适合做矩阵运算。

而神经网络训练和推理中最核心的计算,正是大规模矩阵运算。

当模型参数从几百万增长到几十亿、几百亿、几千亿时,一些过去不明显的能力开始出现。

比如:

  • 语言理解
  • 代码生成
  • 数学推理
  • 多轮对话
  • 工具调用
  • 多模态理解

这些能力看起来像是突然“涌现”出来的。

但它们仍然建立在最底层的二进制计算之上。

十四、计算机的本质:用简单规则堆出复杂世界

回头看整条路径:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
沙子



晶体管

逻辑门

加法器

CPU

内存

指令执行循环

程序

编译器

操作系统

虚拟机

互联网

云计算

神经网络

人工智能

从一粒沙子,到一个可以和人类对话的 AI,中间没有任何一步需要神秘力量。

每一步都是工程上的组合、抽象和放大。

计算机并不真正“懂”文字。

它只是处理数字。

计算机也不真正“懂”图片。

它只是处理像素矩阵。

计算机甚至不真正“懂”程序。

它只是按照规则读取指令、解码指令、执行指令。

但当这些简单操作以极高速度、极大规模运行时,复杂性就出现了。

这就是计算机最迷人的地方。

它的底层极其笨拙:

1
2
3
开,关。
1,0。
是,否。

但它的上层极其宏大:

1
2
3
4
5
6
操作系统
互联网
游戏
搜索引擎
云计算
人工智能

整个计算机文明,就是人类用最简单的开关,一层一层搭出来的巴别塔。

结语

计算机的原理并不神秘。

它不是一开始就拥有智能,也不是天生就会处理复杂任务。

它只是从最简单的电信号开始:

1
2
通电是 1
断电是 0

然后人类用逻辑门构造运算,用内存保存状态,用 CPU 执行指令,用操作系统管理资源,用编译器生产程序,用网络连接世界,最后用海量算力训练出了今天的人工智能。

所以,计算机真正伟大的地方,不是它底层有多聪明。

恰恰相反。

它的底层极其简单,但人类用抽象把这种简单堆成了一个复杂世界。

从开关到 AI,这就是计算机的故事。


参考:能从底层解释一下计算机的原理吗? - sum哈哈的回答 - 知乎

使用OpenGrok部署一套AOSP源码审计站点

一、背景

  做 Android 逆向、系统分析或者漏洞审计的时候,经常需要在大体量源码里快速定位类、方法、调用链和符号定义。单纯依赖本地 IDE 虽然也能看代码,但面对 AOSP 这种规模的工程,索引、跳转、跨模块检索和网页分享都不够方便。

  OpenGrok 是一套源码索引和检索系统,比较适合给 AOSP 这类大型源码仓库建立一套在线源码审计站点。这样做的好处主要有几点:

  1. 可以快速全文检索类名、方法名、字符串常量和路径。
  2. 可以在网页里直接查看定义和引用,适合做源码审计记录。
  3. 可以给多台机器或多人共享,不依赖每个人本地都拉完整源码。
  4. 对超大项目比普通文本检索更友好,结构化能力更强。

二、部署思路

  这篇文章不走传统的 Java + Tomcat 手工部署路线,而是直接使用 Docker 部署 OpenGrok。整个流程可以拆成三步:

  1. 准备 Docker 环境和 OpenGrok 的挂载目录。
  2. 使用 repo 同步 AOSP 源码。
  3. 启动 OpenGrok 容器并为 AOSP 建立索引。

  这样做的好处是环境更干净,迁移也更方便。后面如果要换机器,只需要把源码目录和索引目录带走,再重新起容器就可以。

三、环境准备

1、机器配置

  OpenGrok 本身不算重,但 AOSP 很大,所以真正吃资源的是源码同步和首次全量索引。建议至少准备下面这些资源:

  1. Linux 服务器一台。
  2. 16G 以上内存,内存越大索引过程越舒服。
  3. 8 核以上 CPU,首次索引会快一些。
  4. 足够大的磁盘空间,完整 AOSP 源码和索引数据加起来占用不小。

  如果只是本地测试,配置低一点也能跑;但如果准备长期使用,建议直接上 SSD,不然同步和索引都会比较折磨。

2、安装环境

  • Ubuntu 22.04
  • Docker
  • Git
  • Python3
  • repo 工具

安装 Docker:

1
2
3
4
sudo apt update
sudo apt install -y docker.io
sudo systemctl enable docker
sudo systemctl start docker

安装基础依赖:

1
sudo apt install -y git curl python3

安装 repo:

1
2
3
4
5
mkdir -p ~/bin
curl https://storage.googleapis.com/git-repo-downloads/repo -o ~/bin/repo
chmod a+x ~/bin/repo
echo 'export PATH=$HOME/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

注意事项:

  1. repo 不是 git clone 的别名,它是 AOSP 官方多仓库管理工具。
  2. 如果服务器网络环境比较一般,首次同步 AOSP 可能会很慢,甚至中途失败,重试是正常现象。

3、目录规划

建议先把目录规划好:

1
2
mkdir -p ~/workspace/aosp
mkdir -p ~/opengrok/{src,data,etc}

目录说明:

  1. ~/workspace/aosp:存放 AOSP 源码。
  2. ~/opengrok/src:给 OpenGrok 挂载的源码目录。
  3. ~/opengrok/data:OpenGrok 索引数据目录。
  4. ~/opengrok/etc:OpenGrok 配置目录。

  为了避免重复占空间,后面可以直接把 ~/workspace/aosp 挂到容器里当源码目录使用,不需要再额外拷贝一份。

四、同步 AOSP 源码

  AOSP 代码仓库不是一个单独的 Git 项目,而是一组由 manifest 管理的仓库集合。所以同步 AOSP 的标准姿势,是先 repo init,再 repo sync

1、初始化源码仓库

1
2
cd ~/workspace/aosp
repo init -u https://android.googlesource.com/platform/manifest -b android-14.0.0_r34 --depth=1

这里的几个参数意思比较直观:

  1. -u:指定 manifest 仓库地址。
  2. -b:指定 AOSP 分支或 tag。
  3. --depth:下载当前分支的最新提交

  如果你审计的是 Android 13、Android 12 或者厂商基线,就把 -b 后面的版本换成你自己的目标版本。

2、开始同步源码

1
2
cd ~/workspace/aosp
repo sync -c -j$(nproc) --no-tags --no-clone-bundle

参数说明:

  1. -c:只同步当前分支,减少无关历史。
  2. -j$(nproc):8 个并发线程同步,可以按机器性能调整。
  3. --no-tags:不下载 git tags,进一步压缩体积。
  4. --no-clone-bundle:不使用预打包的克隆包,直接进行流式下载,在某些网络环境下更稳定。

注意事项:

  1. 完整 AOSP 源码体积很大,第一次同步之前一定要先看磁盘空间。
  2. 首次同步耗时很长,卡住一段时间不代表一定出问题。
  3. repo sync 中途中断后,重新执行同一条命令继续同步就可以。
  4. 如果只是做某个版本的源码审计,最好一开始就把版本选准,避免来回切分支重新拉。

3、给 OpenGrok 准备源码挂载

如果你打算直接把 AOSP 源码目录挂载给 OpenGrok,可以创建一个软链接,统一路径也方便后续维护:

1
ln -s ~/workspace/aosp ~/opengrok/src/aosp

这样后面 OpenGrok 就可以直接索引 ~/opengrok/src/aosp 这套源码。

五、使用 Docker 部署 OpenGrok

1、拉取镜像

1
sudo docker pull opengrok/docker:latest

2、启动 OpenGrok

1
2
3
4
5
6
7
sudo docker run -d \
--name opengrok \
-p 8080:8080 \
-v ~/opengrok/src:/opengrok/src \
-v ~/opengrok/data:/opengrok/data \
-v ~/opengrok/etc:/opengrok/etc \
opengrok/docker:latest

这几个参数比较关键:

  1. -p 8080:8080:把 OpenGrok Web 服务暴露到宿主机 8080 端口。
  2. -v ~/opengrok/src:/opengrok/src:挂载源码目录。
  3. -v ~/opengrok/data:/opengrok/data:挂载索引数据目录。
  4. -v ~/opengrok/etc:/opengrok/etc:挂载配置目录。

注意事项:

  1. 源码目录和索引目录最好都做持久化挂载,不要只放在容器里。
  2. 删除容器不会删除挂载目录里的数据,所以后期迁移会方便很多。
  3. 如果 8080 端口被占用,可以改成 -p 8090:8080 这种方式。

3、查看容器状态

1
2
sudo docker ps
sudo docker logs -f opengrok

  容器正常启动之后,后面就可以开始建立索引。

六、构建 AOSP 索引

  索引是整套流程里最核心的一步。AOSP 体量大,第一次全量索引通常会比较耗时,这一步对 CPU、内存和磁盘 I/O 都比较敏感。

1、执行索引命令

1
2
3
4
5
6
sudo docker exec -it opengrok /opt/opengrok/bin/OpenGrok index \
-s /opengrok/src/aosp \
-d /opengrok/data \
-W /opengrok/etc/configuration.xml \
-H -P -S -G \
-U http://127.0.0.1:8080/source

几个关键参数:

  1. -s:源码目录。
  2. -d:索引数据目录。
  3. -W:输出配置文件。
  4. -U:指定 Web 访问路径,便于后面页面加载配置。

  不同镜像版本的 OpenGrok 命令参数可能会有细微差异,如果遇到报错,建议顺手看一下容器内的帮助信息:

1
sudo docker exec -it opengrok /opt/opengrok/bin/OpenGrok index --help

2、观察索引过程

1
sudo docker logs -f opengrok

  第一次索引 AOSP 需要比较长时间,期间重点看这几件事:

  1. ~/opengrok/data 目录是否持续生成索引文件。
  2. 日志里是否有权限错误。
  3. 容器是否因为内存不足被系统杀掉。

3、访问页面验证

索引完成后,浏览器访问:

1
http://服务器IP:8080/source

可以先搜索几个 AOSP 里常见的关键字测试一下:

  1. SystemServer
  2. PackageManagerService
  3. ActivityManagerService

  如果能够正常搜索、点进定义、继续查看引用关系,就说明这套源码审计站点已经能用了。

七、AOSP 更新后的同步与重建索引

  OpenGrok 建好之后,不代表以后就不用管了。只要 AOSP 源码更新了,就需要重新同步并更新索引。

1、同步最新源码

1
2
cd ~/workspace/aosp
repo sync -c -j8

2、重新执行索引

1
2
3
4
5
6
sudo docker exec -it opengrok /opt/opengrok/bin/OpenGrok index \
-s /opengrok/src/aosp \
-d /opengrok/data \
-W /opengrok/etc/configuration.xml \
-H -P -S -G \
-U http://127.0.0.1:8080/source

  如果只是自己内部用,最简单可靠的方案,就是每次 repo sync 之后重新跑一遍索引。后面如果更新频率高,再考虑定时任务或者增量索引。

八、常见问题与注意事项

  1. 源码太大:完整 AOSP 同步和索引都很耗时,要有心理准备。
  2. 磁盘占用高:源码和索引数据加起来会比较可观,最好提前分盘规划。
  3. 容器权限问题:如果挂载目录权限不对,索引时容易报权限错误。
  4. 版本选择问题:不同 Android 大版本最好分开目录、分开站点,后续检索不容易串。
  5. 厂商源码管理:厂商 ROM 和 AOSP 原版最好分开建站,方便做差异对比。
  6. 索引时间长:第一次全量索引很慢,不建议边同步边索引,最好等源码稳定后再一次性建索引。

九、结尾

  以上,就是使用 Docker 部署一套 AOSP OpenGrok 源码审计站点的基本流程。整体思路不复杂,关键就是两件事:先把 AOSP 源码按版本同步好,再把 OpenGrok 的源码目录、索引目录和访问路径规划清楚。

  后面如果还要继续完善,可以再补 Nginx 反向代理、鉴权、定时同步和多版本源码分站这些内容。对于日常源码审计、逆向分析和团队共享来说,这套方案已经足够用了。

十年前的老机器小米4c安装postmarketOS

安装过程可以参考官方链接 https://wiki.postmarketos.org/wiki/Xiaomi_Mi_4c_(xiaomi-libra) 但是很多坑下面会提到

  1. 安装pmbootstrap使用uv装会比较简单

    1
    2
    3
    4
    5
    6
    # 源码安装
    git clone https://gitlab.postmarketos.org/postmarketOS/pmbootstrap.git
    cd pmbootstrap
    mkdir -p ~/.local/bin
    ln -s "$PWD/pmbootstrap.py" ~/.local/bin/pmbootstrap
    pmbootstrap --version
    1
    2
    3
    # uv安装
    uv tool install --python 3.11 git+https://gitlab.postmarketos.org/postmarketOS/pmbootstrap.git
    pmbootstrap --version
  2. 初始化配置

    1
    2
    pmbootstrap -y zap
    pmbootstrap init # 需要国际网络环境

    特别注意:

    • Choose the postmarketOS release channel.
      选edge (只有这个版本才能选libra)
    • Device codename
      选libra (官方文档写错了选xiaomi-ferrari不对)
    • Install systemd
      选never (libra内核版本和systemd不兼容会导致无法编译)
  3. 安装并刷写镜像

    1
    2
    3
    pmbootstrap install
    pmbootstrap flasher flash_kernel
    # pmbootstrap flasher flash_rootfs # 大坑先不要执行

    如果按官方文档刷入rootfs镜像开机则会提示ERROR: root partition not found!找不到root分区,经过研究发现是刷入的镜像格式不对,使用pmbootstrap export命令导出镜像文件存储在/tmp/postmarketOS-export下使用file xiaomi-libra.img可以看到文件是DOS/MBR boot sector; partition 1 : ID=0xee, start-CHS (0x0,0,2), end-CHS (0x3ff,255,63), startsector 1, 1964031 sectors, extended partition table (last) 格式并不是Android设备支持的格式所以直接刷到手机中会出现问题,我们需要从这个文件中再提取出rootfs

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # 查看镜像内部分区
    fdisk -l /tmp/postmarketOS-export/xiaomi-libra.img

    # 假设输出显示分区从 sector 2048 开始
    # 提取纯文件系统部分
    OFFSET=$((2048 * 512))
    LOOP=$(sudo losetup --offset $OFFSET --find --show /tmp/postmarketOS-export/xiaomi-libra.img)

    # 验证
    sudo file -s $LOOP
    # 应该显示: Linux rev 1.0 ext4 filesystem data

    # 导出纯文件系统镜像
    sudo dd if=$LOOP of=/tmp/rootfs-pure.img bs=4M status=progress

    # 释放 loop
    sudo losetup -d $LOOP

    # 用纯镜像刷入
    fastboot flash userdata /tmp/rootfs-pure.img
  4. 最后
    虽然成功解决找不到root分区的问题但是开机黑屏暂时还不知道原因没精力研究了留给后人了

参考:

https://wiki.postmarketos.org/wiki/Xiaomi_Mi_4c_(xiaomi-libra)

https://wiki.postmarketos.org/wiki/SSH

安卓逆向的核心流程

一、背景

  安卓逆向的东西比较多,刚接触容易不知道怎么入手。如果知道安卓逆向的核心流程有哪些,对安卓逆向有一个整体的认知,就能比较清晰地知道自己缺什么和学什么。这篇文章梳理一下安卓逆向的要点,方便学习安卓逆向的小伙伴参考对照。

二、设备

  一开始先准备好手机和电脑这些硬件设备。安卓系统是从linux系统过来的,进程之间互相隔离,想突破隔离就需要有root权限。手机root目前有三种方案:Magisk、KernelSU和APatch。

  1、Magisk:上手比较简单,只需要修改启动文件boot.img,插件也比较多。
  2、KernelSU:修改内核,相对Magisk更加稳定,但门槛高,插件比较少。
  3、APatch:结合上面两个的优势,只需要修改boot.img+支持加载内核模块。

  安卓系统或linux系统分为用户层和内核层,APK只能运行在用户层。如果有内核插件的能力,就可以从更底层进行拦截修改。就逆向分析来说,比较推荐的是APatch:既能够使用上层的安卓插件AndroidPatch Modules(APM),也能够用底层的内核插件KernelPatch Modules(KPM)

三、工具

  拿到一个APK,先看看这个APK有没有加壳。加壳的原理是把原本的代码资源进行加密保护,等到运行的时候再解密释放出来。而脱壳就是把解密释放的代码资源拷贝出来。查壳可以用ApkCheckPack,脱壳可以用frida_dump

  得到没有保护壳的APK之后,就可以进行静态分析,先看看整个APK都有哪些文件资源。APK本身也分为两层:Java层和Native层(C/C++层),对应两种工具:
  1、Java层的静态分析工具:jadx
  2、Native层的静态分析工具:IDA Pro

  静态分析一开始只是看个大概,之后还得进行动态调试,才能知道APK运行的细节。动态调试对应的工具有两种:
  1、单步调试:lldb,适合下断点之后跟踪数据和逻辑
  2、拦截打印:frida,适合拦截看参数和整体调用

  如果一用工具进行分析,就提示环境异常或者直接闪退,那就意味着工具被检测到了。
  检测要么基于原理,比如lldb正在进行动态调试,status会有TracerPid的信息,APK只要检查TracerPid字段,就能知道是不是被动态调试。而反检测就是根据原理,检查的时候修改返回正常的值,比如抹掉TracerPid字段。
  要么是基于特征进行检测,比如frida的默认端口是27042,APK只要检查端口27042是不是存在,就能知道是不是被frida工具入侵了。而反检测就是修改特征值,比如修改frida的端口号;或者直接换魔改版的工具Florida

四、分析

  有了工具,接下来就是进行具体的功能分析,这个时候需要具备两种能力:
  1、正向开发的能力:比如点一个按钮,弹一个提示框。起码要知道正向开发的时候,代码正常是怎么写的,这样逆向分析的时候才能找到切入点。
  2、代码反编译的能力:正向开发是清晰明了的源代码,编译之后看到的是汇编。java编译成smail,Native(C/C++)编译成ARM汇编。而逆向分析就得从底层的这些汇编看出原本代码的逻辑。

  在进行分析代码的时候,也会遇到很多保护手段,给逆向分析带来更多的麻烦:
  1、代码混淆:Java或者Navite(JNI)清晰的名字被替换成abc这些代替符号。这种只能根据代码的逻辑去猜测。
  2、字符串加密:静态分析看到的是混乱的密文,运行的时候才解密成明文。要么运行的时候把明文拷贝出来,要么根据解密算法用脚本去还原。
  3、控制流扁平化:原本结构清晰的ifelse,被打乱成一个状态机不断跳转。现在比较多的是OLLVM的扁平化、对应的可以写脚本去还原原本的结构。
  4、代码变异:原本的代码变异成相同语义的多条代码,一眼看上去不那么直观易懂。这种得走代码优化,或者脚本识别之后的还原。
  5、代码虚拟化:原本的一句代码,变成了一套虚拟代码和虚拟机器,虽然运行的结果一样,但分析难度特别高。这种就得跟踪代码的执行过程,然后根据虚拟机的原理去还原。

五、框架

  分析之后,除非纯协议不依赖客户端,不然就得对客户端的功能进行调用和拦截。如果自用而且功能少,直接按原理写代码也可以。但大多时候功能多,设备复杂,用现成的框架写业务代码更舒服,不用去管底层细节。

  因为APK分成java层和native层,对应的框架也有多种。单纯java层可以用Xposed或者LSPosed;单纯native层可以用Dobby或者Frida Gum;如果java和native都想要,可以用Frida提供的gadget

六、落地

  框架选完,就是落地应用起来。这个时候就得决定最终要运行在什么样的设备环境里。设备环境按root权限可以先分成两种:有root权限和没root权限。

  有root权限具体又分为:真机root和云手机root。
  1、真机root:直接真实的手机进行root,优势是真实机器不用担心设备参数;缺点是支持root的机型有限,目前就pixel和一加手机支持直接root,而且号多会有IP集中的问题。
  2、云手机root:网络云端虚拟出来的手机,优势是可以集中管理,IP分散;缺点是设备参数可能不全或不真实。

  没root权限具体又分为:重打包和虚拟沙箱。
  1、重打包:把APK解开,加入逆向的东西、然后重新签名打包,优势是直接在正常手机上安装运行;缺点是需要过改包的检测。
  2、虚拟沙箱:虚拟APK提供一个虚拟环境,然后加载目标APK,虚拟环境自由注入东西到目标APK里面;好处是不用修改APK,也不需要特殊硬件环境;缺点就是虚拟环境的工作量很大,要做很多接口代理。

  如果只是几个号自己玩,用真实手机root就可以了。如果账号很多需要群控,可以考虑云手机。如果是要在正常手机上运行,因为没有root权限,就得重打包和虚拟沙箱。

七、风控

  不管选择什么样的落地应用方案,多多少少都会和原生APK+真实手机有差异。风控就是开发APK的人不想被逆向破解,在APK里面收集一些关键,看有没有差异。而逆向分析就需要知道风控收集了哪些数据,然后进行隐藏或者修改,尽量模仿:原生APK+真实手机。

  APK收集信息也分为两层:Java层和Native层。这里按读取APK签名信息做例子:
  Java层收集信息:1、直接调用android提供的api:getPackageInfo;2、通过底层的IPC:api最终是通过IPC和系统服务进程通讯拿到数据,应用进程 → Binder IPC → system_server → 返回数据。
  Native层收集信息:1、直接使用syscall:open,read去解析读取;2、比如syscall更底层,直接用汇编指令SVC:mov x8, #56,svc #0。

  想要修改或者隐藏信息,也就得对应拦截:api、IPC、syscall和SVC。前三个都可以在用户层进行拦截,但最后的SVC再下去就是内核。如果是静态的SVC汇编代码,因为地址固定还可以hook;如果是动态生成的,就得在用户层外面拦截了,上内核模块或者定制ROM。此外,linux内核提供了安全模块:seccomp,可以在用户层和内核层之间进行拦截。关键是seccomp的接口暴露给用户层,不用修改内核。

八、结尾

  以上,就是安卓逆向的核心流程。里面的关键点说得很简略,展开说会有很多内容。就像一棵树,先理清主干,之后再开枝散叶。(一堆坑,后续填)


转自:https://bbs.kanxue.com/thread-288487.htm

AI流量审计

Langfuse 简介

Langfuse 是一个开源的 LLM 工程平台(Open Source LLM Engineering Platform),为大语言模型应用提供全面的开发和运维工具。它帮助开发团队监控、调试和优化生产环境中的 LLM 应用。

Langfuse 是干什么的?

当你在做 Chatbot / RAG / Agent / 多步推理应用 时,常见问题是:

模型为什么这次回答这么差?
Prompt 改了到底有没有变好?
用户问了什么?模型是怎么一步步走的?
Token 花在哪里了?成本多少?
不同模型 / Prompt 哪个更好?

👉 Langfuse 就是用来解决这些问题的。

核心功能

1. 可观测性(Observability)

  • 完整的应用追踪:基于 OpenTelemetry 标准,提供嵌套追踪能力
  • 性能监控:显示每个调用的延迟和成本
  • 简单集成:通过 @observe() 装饰器即可集成

2. 评估系统(Evaluation)

  • 自动化评估框架
  • 人工标注功能
  • 数据集管理和版本控制

3. 提示词管理(Prompt Management)

  • 集中管理提示词模板
  • 版本控制和回滚
  • Playground 测试环境

4. 指标分析(Metrics)

  • 实时性能监控和分析
  • 详细的成本追踪
  • 自定义指标统计

集成生态

Langfuse 支持主流的 LLM 开发框架和工具:

  • SDK:Python、JavaScript/TypeScript
  • 模型提供商:OpenAI、Anthropic 等
  • 框架:LangChain、LlamaIndex、Vercel AI SDK、Instructor
  • 网关:LiteLLM
  • 无代码工具:Dify、Flowise、Langflow

主要优势

  1. 易于集成:提供 drop-in wrapper,最小化代码改动
  2. 开源透明:支持自托管部署,符合多项安全认证(SOC 2 Type II、ISO 27001、GDPR、HIPAA)
  3. 全栈覆盖:从开发到生产的完整工具链
  4. 调试友好:详细的追踪信息帮助快速定位问题

Langfuse 自托管部署

本部署方案采用 Langfuse + LiteLLM 的组合架构,实现对 LLM 应用流量的完整审计和可观测性。

架构说明

这个部署方案包含三个核心服务:

  1. PostgreSQL 数据库(db)

    • 存储 Langfuse 的所有数据
    • 包括 traces、用户数据、配置等
  2. Langfuse Web 服务(langfuse-web)

    • 提供可视化界面和 API
    • 用于查看追踪数据、管理提示词等
    • 访问地址:http://localhost:3000
  3. LiteLLM 代理层(litellm-proxy)

    • 统一的 LLM 网关,支持多个模型提供商
    • 自动将所有 LLM 调用发送到 Langfuse 进行追踪
    • 访问地址:http://localhost:4000

工作流程

1
2
3
你的应用 → LiteLLM 代理 → 各种 LLM API (OpenAI/Claude/等)

Langfuse 追踪和分析

通过这种架构,你的应用只需要配置 LiteLLM 的地址,所有 LLM 调用都会被自动记录和分析。


快速部署

步骤 1:准备配置文件

创建 docker-compose.yml 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
version: '3.9'

services:
# --- Langfuse 数据库 ---
db:
image: postgres:16
restart: unless-stopped
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=langfuse
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5

# --- Langfuse 服务端 (http://localhost:3000) ---
langfuse-web:
image: langfuse/langfuse:2
restart: unless-stopped
depends_on:
db:
condition: service_healthy
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/langfuse
- NEXTAUTH_SECRET=mysecret # 生产环境请修改
- SALT=mysalt # 生产环境请修改
- NEXTAUTH_URL=http://localhost:3000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/public/health"]
interval: 30s
timeout: 10s
retries: 3

# --- LiteLLM 代理层 (http://localhost:4000) ---
litellm-proxy:
image: ghcr.io/berriai/litellm:main-latest
restart: unless-stopped
depends_on:
langfuse-web:
condition: service_healthy
ports:
- "4000:4000"
volumes:
- ./litellm-config.yaml:/app/config.yaml
command: ["--config", "/app/config.yaml"]
environment:
# 注意:这些密钥需要在 Langfuse 启动后从后台获取
- LANGFUSE_PUBLIC_KEY=pk-lf-your-public-key
- LANGFUSE_SECRET_KEY=sk-lf-your-secret-key
- LANGFUSE_HOST=http://langfuse-web:3000

volumes:
pgdata:

步骤 2:创建 LiteLLM 配置文件

创建 litellm-config.yaml 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
model_list:
# OpenAI 模型配置
- model_name: gpt-4
litellm_params:
model: openai/gpt-4
api_key: os.environ/OPENAI_API_KEY

- model_name: gpt-3.5-turbo
litellm_params:
model: openai/gpt-3.5-turbo
api_key: os.environ/OPENAI_API_KEY

# Anthropic Claude 配置
- model_name: claude-3-opus
litellm_params:
model: anthropic/claude-3-opus-20240229
api_key: os.environ/ANTHROPIC_API_KEY

- model_name: claude-3-sonnet
litellm_params:
model: anthropic/claude-3-sonnet-20240229
api_key: os.environ/ANTHROPIC_API_KEY

# LM Studio / vllm / ollama
- model_name: qwen3-vl-32b-thinking # 你在 Agent 中调用的模型名
litellm_params:
model: openai/qwen3-vl-32b-thinking # 格式为 openai/任意名
api_base: http://127.0.0.1:1234/v1 # 填写你现有的 vLLM 地址
api_key: "not-needed"

# Langfuse 集成配置(自动追踪所有请求)
litellm_settings:
success_callback: ["langfuse"]
failure_callback: ["langfuse"]

# 可选:启用缓存
cache: true
cache_params:
type: "redis" # 或者 "disk"

# 可选:日志配置
general_settings:
master_key: "sk-1234" # 访问 LiteLLM 的密钥
database_url: "postgresql://postgres:postgres@db:5432/litellm"

步骤 3:配置环境变量

创建 .env 文件存储敏感信息:

1
2
3
4
5
6
7
8
9
10
11
# LLM API 密钥
OPENAI_API_KEY=sk-your-openai-key
ANTHROPIC_API_KEY=sk-ant-your-anthropic-key

# Langfuse 密钥(第一次启动后获取)
LANGFUSE_PUBLIC_KEY=pk-lf-your-public-key
LANGFUSE_SECRET_KEY=sk-lf-your-secret-key

# 生产环境安全配置
NEXTAUTH_SECRET=$(openssl rand -base64 32)
SALT=$(openssl rand -base64 32)

步骤 4:首次启动(获取 Langfuse 密钥)

1
2
3
4
5
# 第一次启动(不包括 LiteLLM)
docker compose up db langfuse-web -d

# 等待服务启动完成
docker compose logs -f langfuse-web

访问 http://localhost:3000:

  1. 注册账号并登录
  2. 进入 SettingsAPI Keys
  3. 创建新的 API Key
  4. 复制 Public Key (pk-lf-…) 和 Secret Key (sk-lf-…)
  5. 更新 .env 文件或 docker-compose.yml 中的密钥

步骤 5:启动完整服务

1
2
3
4
5
6
7
8
9
# 更新配置后重新启动所有服务
docker compose down
docker compose up -d

# 查看服务状态
docker compose ps

# 查看日志
docker compose logs -f

配置详解

1. PostgreSQL 数据库配置

1
2
3
4
5
6
7
8
db:
image: postgres:16
environment:
- POSTGRES_USER=postgres # 数据库用户名
- POSTGRES_PASSWORD=postgres # 数据库密码(生产环境必须修改)
- POSTGRES_DB=langfuse # 数据库名称
volumes:
- pgdata:/var/lib/postgresql/data # 数据持久化

注意事项:

  • 生产环境必须修改默认密码
  • 定期备份 pgdata
  • 可以配置端口映射进行外部访问(不推荐)

2. Langfuse Web 服务配置

1
2
3
4
5
6
7
8
9
10
langfuse-web:
image: langfuse/langfuse:2
environment:
# 数据库连接(格式:postgresql://用户:密码@主机:端口/数据库)
- DATABASE_URL=postgresql://postgres:postgres@db:5432/langfuse

# NextAuth 配置
- NEXTAUTH_SECRET=mysecret # 用于 JWT 签名(必须修改)
- SALT=mysalt # 密码加盐(必须修改)
- NEXTAUTH_URL=http://localhost:3000 # 服务访问地址

关键环境变量说明:

变量 说明 生产环境建议
DATABASE_URL PostgreSQL 连接字符串 使用强密码
NEXTAUTH_SECRET JWT 签名密钥 openssl rand -base64 32 生成
SALT 密码加盐值 openssl rand -base64 32 生成
NEXTAUTH_URL 服务外部访问地址 使用实际域名,如 https://langfuse.example.com

可选配置:

1
2
3
4
5
6
7
8
9
10
11
environment:
# 禁用公开注册
- AUTH_DISABLE_SIGNUP=true

# 配置 SMTP 邮件服务
- SMTP_CONNECTION_URL=smtp://user:pass@smtp.example.com:587
- EMAIL_FROM_ADDRESS=noreply@example.com

# 启用 Google SSO
- AUTH_GOOGLE_CLIENT_ID=your-client-id
- AUTH_GOOGLE_CLIENT_SECRET=your-client-secret

3. LiteLLM 代理配置

1
2
3
4
5
6
7
8
9
10
litellm-proxy:
image: ghcr.io/berriai/litellm:main-latest
volumes:
- ./litellm-config.yaml:/app/config.yaml # 挂载配置文件
command: ["--config", "/app/config.yaml"]
environment:
# Langfuse 集成配置
- LANGFUSE_PUBLIC_KEY=pk-lf-xxx # 从 Langfuse 后台获取
- LANGFUSE_SECRET_KEY=sk-lf-xxx # 从 Langfuse 后台获取
- LANGFUSE_HOST=http://langfuse-web:3000 # Langfuse 服务地址

LiteLLM 配置文件详解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# litellm-config.yaml
model_list:
# 定义可用的模型
- model_name: gpt-4 # 你的应用中使用的模型名称
litellm_params:
model: openai/gpt-4 # 实际调用的模型
api_key: os.environ/OPENAI_API_KEY # API 密钥(从环境变量读取)

litellm_settings:
# 自动将所有调用发送到 Langfuse
success_callback: ["langfuse"] # 成功时回调
failure_callback: ["langfuse"] # 失败时回调

general_settings:
master_key: "sk-1234" # LiteLLM 访问密钥(可选,用于认证)

支持的模型提供商:

  • OpenAI (gpt-4, gpt-3.5-turbo 等)
  • Anthropic (claude-3-opus, claude-3-sonnet 等)
  • Azure OpenAI
  • Google PaLM
  • Cohere
  • Replicate
  • 更多…

在 Langfuse 中查看追踪

  1. 访问 http://localhost:3000
  2. 登录后进入 Traces 页面
  3. 查看所有 LLM 调用的详细信息:
    • 请求参数和响应
    • 延迟时间
    • Token 使用量
    • 成本统计

参考资料

基于工作资料实现应用多开

概述

Android 系统的工作资料(Work Profile)功能可以为应用创建独立的运行环境,实现应用双开。本文介绍如何通过 ADB 命令手动创建工作资料,并安装应用实现多开。

实现原理

工作资料是 Android 5.0 引入的企业功能,本质上是创建一个受管理的用户配置文件(Managed Profile)。通过 pm create-user 命令创建托管配置文件,然后将应用安装到该配置文件中,实现与主用户空间的应用完全隔离。

前置条件

  • Android 5.0 及以上系统
  • 启用 USB 调试
  • 安装 ADB 工具
  • 设备未设置工作资料(一台设备只能有一个工作资料)

操作步骤

1. 创建工作资料

使用以下命令创建一个托管配置文件:

1
adb shell pm create-user --profileOf 0 --managed 工作资料名称

参数说明:

  • --profileOf 0:指定父用户为用户 0(主用户)
  • --managed:创建托管配置文件(工作资料)
  • 工作资料名称:自定义名称,例如”工作空间”、”Work”等

示例:

1
adb shell pm create-user --profileOf 0 --managed Work

执行成功后会返回创建的用户 ID,例如:

1
Success: created user id 13

记录这个用户 ID(本例中为 13),后续操作需要用到。

2. 启动工作资料

创建完成后需要启动工作资料:

1
adb shell am start-user 13

13 替换为上一步返回的实际用户 ID。

3. 查看所有用户

可以使用以下命令查看系统中的所有用户:

1
adb shell pm list users

输出示例:

1
2
3
Users:
UserInfo{0:Owner:c13} running
UserInfo{13:Work:1030} running

可以看到主用户(ID 为 0)和刚创建的工作资料(ID 为 13)都在运行中。

4. 安装应用到工作资料

方法一:复制已安装的应用

如果主用户空间已经安装了某个应用,可以直接将其复制到工作资料中:

1
adb shell pm install-existing --user 13 com.tencent.mm

参数说明:

  • --user 13:指定目标用户 ID(工作资料)
  • com.tencent.mm:应用包名(此处以微信为例)

常用应用包名:

  • 微信:com.tencent.mm
  • QQ:com.tencent.mobileqq
  • WhatsApp:com.whatsapp
  • Telegram:org.telegram.messenger

方法二:直接安装 APK

也可以直接将 APK 安装到工作资料中:

1
adb install --user 13 /path/to/app.apk

5. 验证安装

安装完成后,可以查看工作资料中已安装的应用:

1
adb shell pm list packages --user 13

工作资料中的应用会在桌面显示,图标带有工作资料标识(通常是公文包图标)。

完整示例

以下是创建工作资料并安装微信的完整命令流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 创建工作资料
adb shell pm create-user --profileOf 0 --managed Work

# 假设返回用户 ID 为 13

# 2. 启动工作资料
adb shell am start-user 13

# 3. 复制微信到工作资料
adb shell pm install-existing --user 13 com.tencent.mm

# 4. 查看工作资料中的应用列表
adb shell pm list packages --user 13

管理工作资料

停止工作资料

1
adb shell am stop-user 13

停止后,工作资料中的应用将停止运行,不会收到通知。

启动工作资料

1
adb shell am start-user 13

删除工作资料

如果不再需要工作资料,可以删除:

1
adb shell pm remove-user 13

注意:删除工作资料会清除其中所有应用和数据,操作前请备份重要数据。

从工作资料卸载应用

1
adb shell pm uninstall --user 13 com.tencent.mm

三星手机基于DualMessage实现任意应用双开

概述

Android 系统的多用户机制为应用双开提供了原生支持。三星的 Dual Messenger 功能就是基于此原理实现的。本文将介绍如何利用 Android 多用户机制,突破官方限制,为任意应用实现双开功能。

实现原理

Android 系统支持多用户功能,每个用户拥有独立的应用空间和数据。三星 Dual Messenger 通过创建一个名为 “DUAL_APP” 的特殊用户(通常用户 ID 为 95),在该用户空间中安装应用的第二个实例,从而实现双开。

官方仅支持特定应用(如 WhatsApp、Facebook Messenger 等)的双开,这些应用的白名单硬编码在 /system/app/DAAgent/DAAgent.apkWhiteListApps 类中。通过 ADB 命令,我们可以绕过这个限制,为任意应用创建双开实例。

基础方法:ADB 安装应用到双开空间

前置条件

  • 启用 USB 调试
  • 安装 ADB 工具
  • 至少启用一个官方支持的双开应用(如 WhatsApp)

操作步骤

  1. 查找双开用户 ID

    1
    adb shell pm list users

    输出示例:

    1
    2
    3
    Users:
    UserInfo{0:Owner:c13} running
    UserInfo{95:DUAL_APP:1030} running

    记录 DUAL_APP 的用户 ID(通常是 95)

  2. 安装应用到双开空间

    1
    adb install --user 95 /path/to/app.apk

    安装成功后,应用会出现在双开空间中,图标带有双开标识。

权限配置

方法一:通过系统设置授权

新安装的双开应用默认没有任何权限,需要手动授予:

  1. 进入 设置权限管理器
  2. 选择需要授予的权限类型(如联系人、存储等)
  3. 找到双开应用实例
  4. 点击 查看所有权限 进入完整的权限设置页面
  5. 逐一授予所需权限

方法二:通过 ADB 命令授权

更快捷的方式是使用 ADB 命令直接授权:

1
2
3
4
5
6
7
8
9
10
11
# 授予存储读取权限
adb shell pm grant --user 95 com.package.name android.permission.READ_EXTERNAL_STORAGE

# 授予存储写入权限
adb shell pm grant --user 95 com.package.name android.permission.WRITE_EXTERNAL_STORAGE

# 授予相机权限
adb shell pm grant --user 95 com.package.name android.permission.CAMERA

# 授予联系人权限
adb shell pm grant --user 95 com.package.name android.permission.READ_CONTACTS

com.package.name 替换为实际的应用包名。

高级方案:使用 Aurora Store

为了方便在双开空间中安装和管理应用,可以将 Aurora Store 安装到双开空间。

安装 Aurora Store

  1. 下载 Aurora Store APK

  2. 安装到双开空间

    1
    adb install --user 95 aurora.apk
  3. 授予必要权限

    1
    2
    3
    4
    5
    6
    # 授予存储权限
    adb shell pm grant --user 95 com.aurora.store android.permission.READ_EXTERNAL_STORAGE
    adb shell pm grant --user 95 com.aurora.store android.permission.WRITE_EXTERNAL_STORAGE

    # 授予安装应用权限
    adb shell appops set --user 95 com.aurora.store REQUEST_INSTALL_PACKAGES allow
  4. 使用 Aurora Store

    打开双开空间中的 Aurora Store,即可直接下载安装应用,无需每次都通过 ADB。

分离 APK 的处理

部分现代应用使用分离 APK(Split APKs)格式,无法直接通过 adb install 安装。需要使用专门的安装器。

使用 SAI(Split APKs Installer)

  1. 安装 SAI 到双开空间

    1
    adb install --user 95 sai.apk
  2. 授予权限

    1
    2
    3
    adb shell pm grant --user 95 com.aefyr.sai android.permission.READ_EXTERNAL_STORAGE
    adb shell pm grant --user 95 com.aefyr.sai android.permission.WRITE_EXTERNAL_STORAGE
    adb shell appops set --user 95 com.aefyr.sai REQUEST_INSTALL_PACKAGES allow
  3. 安装分离 APK

    • 下载应用的 .apkm.apks 文件
    • 将文件移动到双开空间的下载目录
    • 使用双开版 SAI 打开并安装

技术深入:修改白名单(需要 Root)

对于已 Root 的设备,可以通过修改系统文件永久添加应用到白名单:

  1. 系统服务 dual_app 维护着支持双开的应用列表
  2. 白名单定义在 /system/app/DAAgent/DAAgent.apk
  3. 可以通过反编译 APK,修改 WhiteListApps 类,重新打包并替换系统文件
  4. 或使用 Xposed/Magisk 模块动态修改白名单

注意:修改系统文件有风险,操作前请做好备份。

注意事项

  1. 兼容性:不同厂商的 Android 系统对多用户的实现可能有差异,本方法主要适用于三星设备
  2. 稳定性:非官方双开的应用可能存在稳定性问题,建议先测试
  3. 数据安全:双开空间的数据独立存储,卸载应用会清除所有数据,注意备份

参考资料

Git项目管理

使用Git提交模板

  1. 项目根目录下创建提交模板

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    cat > .gitmessage << 'EOF'
    # ============= Git 提交信息模板 =============
    # 类型(必填): feat | fix | docs | style | refactor | perf | test | chore | ci | build | revert
    # 影响范围(可选): 例如模块、组件或功能(如 "user-auth", "payment-api"
    # 描述(必填): 简洁说明本次提交的更改
    # --------------------------------------------

    <类型>(<影响范围>): <简短描述>

    # 详细说明(可选):
    # - 为什么进行这次更改?
    # - 解决了什么问题?
    # - 是否有副作用或注意事项?

    # 关联 Issue 或 Task(可选):
    # Fixes #123
    # Closes #456
    # Related to #789

    # -------------------- 示例 --------------------
    # feat(user-auth): 添加 OAuth2 登录支持
    #
    # - 集成 Google 和 GitHub OAuth2 登录
    # - 更新用户模型以存储第三方登录信息
    #
    # Fixes #42
    # Related to #56
    # =============================================
    EOF
  2. 配置当前项目使用模板提交(项目成员都需配置)

    1
    git config --local git commit.template=.gitmessage
  3. 创建强制检查脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    mkdir .githooks
    cat > commit-msg << 'EOF'
    #!/bin/bash
    MSG_FILE="$1"
    MSG=$(cat "$MSG_FILE")

    # 检查是否包含类型和描述
    if ! echo "$MSG" | grep -qE "^(feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert)\(.*\): .+"; then
    echo "错误:提交信息不符合规范!"
    echo "示例: feat(user-auth): 添加登录功能"
    exit 1
    fi
    EOF
    chmod +x .githooks/commit-msg
  4. 配置强制检查提交格式(项目成员都需配置)

    1
    git config --local git core.hookspath=.githooks

Linux上支持拆分窗口的终端

在Ubuntu中,有几个终端模拟器支持窗口拆分功能,允许你在一个终端窗口内同时显示多个会话。以下是一些常见的支持窗口拆分的终端模拟器:

  1. GNOME Terminal
    • GNOME Terminal本身不直接支持窗口拆分,但你可以使用tmuxscreen等终端复用工具来实现窗口拆分功能。
  2. Tilix
    • Tilix是一个非常强大的终端模拟器,支持平铺式布局和窗口拆分。你可以同时在同一窗口中垂直或水平拆分多个终端会话,非常适合进行多任务操作。
  3. Terminator
    • Terminator也是一个支持窗口拆分的终端模拟器,它允许你将终端拆分成多个窗格,可以水平或垂直拆分,十分方便进行多任务操作。
  4. Konsole
    • 如果你使用的是KDE桌面环境,Konsole是一个非常好的选择,它也支持窗口拆分功能,用户可以在一个窗口内分屏显示多个终端会话。
  5. Alacritty
    • Alacritty本身不内置窗口拆分功能,但可以与tmux结合使用,达到类似的效果。Alacritty本身强调速度和简洁性,因此没有过多的图形化界面。
  6. Xterm + tmux/screen
    • Xterm 是一个轻量级的终端模拟器,虽然它没有直接的窗口拆分功能,但可以与tmuxscreen等工具结合使用,提供类似的终端拆分体验。

如果你需要一个图形化界面的终端模拟器,TilixTerminator都是非常好的选择。如果你不介意使用命令行工具,结合tmux的任何终端模拟器都能满足拆分窗口的需求。

  • Copyrights © 2015-2026 Kaisar
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信