eBPF安卓辅助工具:上 [初级使用]
Stackplz 所有参数说明
Usage:
stackplz [flags]
使用方式:运行 stackplz 时可加各种参数。
-a, --arch string targe arch, aarch64, arm/aarch32 (default "aarch64")
指定目标架构,如 aarch64 或 arm(默认 aarch64)。
--auto auto resume when use --kill SIGSTOP
在命中断点发送 SIGSTOP 后自动恢复程序运行。
--brk string set hardware breakpoint address
设置硬件断点地址,用于调试。
--brk-len uint hardware breakpoint length, default 4, support [1, 8] (default 4)
设置硬件断点长度,单位字节,支持 1~8(默认 4 字节)。
--brk-lib string as library base address, work with -p/--pid option
设置库的基地址,用于断点定位(配合 PID 使用)。
--brk-pid int set hardware breakpoint pid, just keep default (default -1)
设置断点关联的 PID,默认自动选择(-1)。
--btf declare BTF enabled
启用内核 BTF 类型信息(供 BPF 使用)。
-b, --buffer uint32 perf cache buffer size, default 8M (default 8)
设置 perf 缓存缓冲区大小(单位 MB,默认 8MB)。
--color enable color for log file
启用日志文件的彩色输出。
-c, --config stringArray hook config file
指定 hook 配置文件(可多个)。
-d, --debug enable debug logging
启用调试模式,输出更多调试信息。
--dump string save perf data to file
将 perf 原始数据保存到文件。
--dumphex dump buffer as hex
以十六进制格式输出缓冲区内容。
--dumpret dump ret offset for symbol
输出符号函数的返回偏移量。
-f, --filter stringArray arg filter rule
设置函数参数过滤规则。
--full-tname disable default thread name black list
取消默认线程名黑名单,允许更多线程被跟踪。
--getoff try get pc and lr offset
尝试获取 PC 和 LR 寄存器的偏移信息。
-h, --help help for stackplz
显示帮助信息。
-j, --json log event as json format
以 JSON 格式记录事件日志。
--kill string send signal when hit uprobe hook, e.g. SIGSTOP/SIGABRT/SIGTRAP/...
当命中 uprobe 时发送信号(如 SIGSTOP、SIGABRT)。
-l, --lib string lib name or lib full path, default is libc.so (default "libc.so")
指定库名或完整路径(默认 libc.so),用于 hook。
--maxop uint32 max operation count for uprobe, at least 192 for string array (default 64)
设置最大操作次数(对字符串数组至少需 192)。
--mstack manual parse stack
手动解析堆栈,便于调试栈内容。
-n, --name string must set uid or package name
必须指定 UID 或包名,用于标识目标。
--no-pid string pid black list
设置 PID 黑名单,排除某些进程。
--no-syscall string syscall black list, max 20
设置系统调用黑名单(最多支持 20 个)。
--no-tid string tid black list
设置线程 TID 黑名单。
--no-tname string thread name black list
设置线程名黑名单。
--no-uid string uid black list
设置 UID 黑名单,排除指定用户。
--nocheck disable check for bpf
跳过 BPF 检查,加快加载速度。
-o, --out string save the log to file
将日志保存到指定文件。
--parse string parse perf data as json or readable format
将 perf 数据解析为 JSON 或可读格式。
-p, --pid string pid white list
设置 PID 白名单,仅监控这些进程。
-w, --point stringArray hook point config, e.g. strstr+0x0[str,str] write[int,buf:128,int]
设置 hook 点配置,例如函数名+偏移和参数结构。
-q, --quiet wont logging to terminal when used
静默模式,不在终端输出日志。
--reg string get the offset of reg
获取寄存器偏移值。
--regs show regs
显示寄存器状态。
--rpc enable rpc
启用 RPC 远程控制模式。
--rpc-path string rpc path, default 127.0.0.1:41718 (default "127.0.0.1:41718")
设置 RPC 地址(默认 127.0.0.1:41718)。
--showpc show origin pc register value
显示原始 PC 寄存器的值。
--showtime show event boot time info
显示事件发生时的系统启动时间。
--showuid show process uid info
显示进程的 UID 信息。
--stack enable unwindstack
启用堆栈解析(unwindstack)。
--stack-size uint32 stack dump size, default 8192 bytes, max 65528 bytes (default 8192)
设置堆栈转储的大小,默认 8192 字节,最大支持 65528。
-s, --syscall string filter syscalls
设置系统调用过滤规则,仅分析这些 syscall。
-t, --tid string tid white list
设置线程 TID 白名单,仅监控这些线程。
--tkill string send signal to thread when hit uprobe hook, e.g. SIGSTOP/SIGABRT/SIGTRAP/...
命中 uprobe 时向线程发送信号。
--tname string thread name white list
线程名白名单,仅包含指定线程名。
-u, --uid string uid white list
UID 白名单,仅监控这些用户进程。
使用 eBPF 追踪某个包的 /data/local 路径写入.
./stackplz -n com.greenpoint.android.mc10086.activity -w open64[str,int] -f w:/data/local
[31967|31967|c10086.activity] open64(arg_0=0x7733810270(
/data/app/~~vHS6VOfMouhquTYnpJbNig==
/com.greenpoint.android.mc10086.activity-jUZ9TI4kIbZzFdwZzDTr-A==
/lib/arm64/libexec.so), arg_1=0)
LR:0x7631eab5d8 PC:0x7905673a70 SP:0x7fcc8a58e0
[31967|31967|c10086.activity] open64(arg_0=0x7733810270(
/data/app/~~vHS6VOfMouhquTYnpJbNig==
/com.greenpoint.android.mc10086.activity-jUZ9TI4kIbZzFdwZzDTr-A==
/lib/arm64/libexecmain.so), arg_1=0)
LR:0x7631eab5d8 PC:0x7905673a70 SP:0x7fcc8a58e0
./stackplz -n com.greenpoint.android.mc10086.activity -w open64[str.f0,int] -f w:/data
./stackplz -n com.greenpoint.android.mc10086.activity -w open64[str.f0.f1.f2,int] -f w:/data -f
ADRP LDR ADD BR 指令
cat /proc/31505/maps | grep libssl.so
SSL_read ;
30 00 00 90 ADRP x16, off_5B540@PAGE
11 A2 42 F9 LDR X17, |X16, #off_5B540@PAGE
10 02 15 91 ADD X16,X16, #off_5B540@PAGE
20 02 1F D6 BR X17
30 00 00 90 → ADRP x16, off_5B540@PAGE | 计算 off_5B540 的 页基址 并存入 x16 |
11 A2 42 F9 → LDR X17, [X16, #off_5B540@PAGE] | 从 x16 计算出的地址 加载 X17,X17 现在存储的是 SSL_read 的 实际地址 |
10 02 15 91 → ADD X16,X16, #off_5B540@PAGE | 计算完整的 off_5B540 地址 |
20 02 1F D6 → BR X17 | 跳转到 X17 存储的地址,即 SSL_read |
ADRP LDR ADD BR 可能的作用
这段代码是 间接调用 SSL_read 的跳转表部分:
SSL_read可能是一个 动态链接的符号(比如通过libssl.so提供)。- IDA 可能在分析时还没有解析出
off_5B540对应的具体地址。
2. uprobe hook
./stackplz -n com.sfx.ebpf --point strstr[str,str] --getoff --stack
--getoff try get pc and lr offset
--stack enable unwindstack
-w, --point stringArray hook point config, e.g. strstr+0x0[str,str] write[int,buf:128,int]
-n, --name string must set uid or package name
lr(Link Register,链接寄存器) lr 存储 函数调用返回地址。 当执行 BL(Branch with Link)指令时,CPU 会自动把当前 PC(程序计数器)存入 lr,然后跳转到目标地址。 当函数执行完毕,使用 RET 指令,它会跳转回 lr 指定的地址。
pc(Program Counter,程序计数器)
pc 存储 当前执行指令的地址,也就是 CPU 当前正在运行的指令地址。
每次执行一条指令,pc 都会自动递增(通常 +4 字节),除非遇到跳转指令。
pc 不能直接修改,但可以通过 BR、BL、RET 以及异常(如 SVC、IRQ)间接改变。
sp(Stack Pointer,栈指针)
sp 指向 当前栈顶,用于函数调用时保存局部变量、返回地址等。
在 Arm64 里,sp 必须 16 字节对齐。
常见操作:
STP(Store Pair):保存寄存器到栈
LDP(Load Pair):从栈恢复寄存器
SUB sp, sp, #16:分配栈空间
ADD sp, sp, #16:释放栈空间
![eBPF安卓辅助工具:上 [初级使用]](https://market.nullflag.com/wp-content/themes/sage/themer/assets/images/lazy.png)
./stackplz -n com.sfx.ebpf -l libnative-lib.so -w _Z5func4i[int] --regs
-n, --name string must set uid or package name
-l, --lib string lib name or lib full path, default is libc.so (default "libc.so")
-w, --point stringArray hook point config, e.g. strstr+0x0[str,str] write[int,buf:128,int]
--regs show regs
![eBPF安卓辅助工具:上 [初级使用]](https://market.nullflag.com/wp-content/themes/sage/themer/assets/images/lazy.png)
./stackplz -n com.sfx.ebpf -w open64[str,int] -f w:/data
./stackplz -n com.sfx.ebpf -w open64[str.f0,int] -f w:/data
./stackplz -n com.sfx.ebpf -w open64[str.f0.f1.f2,int] -f w:/data -f b:/sys -f w:/proc
-n, --name string must set uid or package name
-w, --point stringArray hook point config, e.g. strstr+0x0[str,str] write[int,buf:128,int]
-f, --filter stringArray arg filter rule
![eBPF安卓辅助工具:上 [初级使用]](https://market.nullflag.com/wp-content/themes/sage/themer/assets/images/lazy.png)
说明:
--getoff 计算LR与PC的偏移情况,方便快速定位调用位置
-l/--lib 指定动态库路径,默认为libc.so
大多数情况下使用库名即可,如果遇到同名不同路径,或者找不到的情况,可手动指定为/proc/{pid}/maps中的完整路径
-w/--point 意为观测点,配合动态库指定符号或者偏移,可设置多个,上限6个
语法是{符号/基址偏移}{+偏移}{[参数类型{寄存器/读取地址},参数类型{.f{规则索引}}]}
一个观测点下最多读取10个参数
一个参数下最多应用6个过滤规则
下面是一些使用组合示例,目前支持的类型:int,uint,int64,uint64,str,ptr,buf
-w _Z5func1v
-w 0x9542c[str,str]
-w strstr+0x4[str,str]
-w strstr[str,str] -w open[str,int]
-w 0xA94E8[int:x1,ptr:sp+0x30-0x2c]
默认按寄存器顺序读取,也可以参考本示例,在第一个位置读取x1为int,在第二个位置读取sp+0x30-0x2c为指针
没错,可以进行简单的计算,这样能够灵活读取栈上的内容
-w write[int,buf:x2,int]
-w write[int,buf:32,int]
-w write[int,buf:0x10,int]
buf较为特殊,规则如下:
如果:后面是寄存器表明以该寄存器大小为长度读取数据
如果:后面是十进制或十六进制字符串,则表明以这个数的大小为长度读取数据
-w write[buf:x2:x1,int,int]
这里的规则表示,读取第一个参数时,读的是x1寄存器,长度为x2,即:后的第一个内容表示长度,末尾的才是要读取的寄存器
-f/--filter 字符串过滤规则,有三种,分别是w/white b/black r/replace,示例如下
规则位于参数的末尾,多个规则以.作为分隔符
替换规则涉及两个字符串,以:::作为分隔符
示例如下,依次为白名单规则、黑名单规则、替换规则:
-f w:/data # 白名单, 包含 data 就会被输出出来.
-f b:/sys
-f r:/system/bin/su:::/system/bin/zz
-w openat[int,str.f0.f1] 意为对符号openat的第二个参数,应用规则0和规则1
-s openat:f0 这是syscall,先在这里说了,意为对openat系统调用的第一个字符串参数应用规则0,注意分隔符为:
./stackplz -n com.sfx.ebpf -s openat,socket,connect
./stackplz -n com.greenpoint.android.mc10086.activity -s openat,socket,connect
# 虽然 -s 是hook 系统调用, 但是本质是 hook 了所有的系统调用, 只是显示过滤了这3个.
[23976|23976|c10086.activity] openat(dirfd=-100, *pathname=0x7733810270(/data/app/~~vHS6VOfMouhquTYnpJbNig==/com.greenpoint.android.mc10086.activity-jUZ9TI4kIbZzFdwZzDTr-A==/lib/arm64/libexec.so), flags=0x0, mode=0o000) LR:0x7905673b4c PC:0x79056bfed8 SP:0x7fcc8a57b0
[23976|23976|c10086.activity] openat(dirfd=-100, *pathname=0x7733810270, flags=0x0, mode=0o000, ret=76)
[23976|23976|c10086.activity] openat(dirfd=-100, *pathname=0x7733810d20(/data/app/~~vHS6VOfMouhquTYnpJbNig==/com.greenpoint.android.mc10086.activity-jUZ9TI4kIbZzFdwZzDTr-A==/lib/arm64/libexec.so), flags=0x80000(O_CLOEXEC), mode=0o000) LR:0x790c8c2a30 PC:0x790c8d7478 SP:0x7fcc8a3a40
libssl, libart中符号捞取和偏移地址定位

符号hook, inline hook 输出汇编参数返回值.
当然,从数据变化来看,印度的互联网基础建设的确大幅提升。但在印度,我们动辄遭遇手机全天无信号的状态,就连从北京带去印度的移动wifi也几近变成了废砖头。不难想象,UC浏览器带来的高速浏览和低流量消耗,在目前的印度市场上仍具竞争力。
案例1: hook SSL_write实现简易 eCapture.
.../local/tmp # ps -e | grep -i 10086
23701 ? 00:01:42 com.greenpoint.android.mc10086.activity
23847 ? 00:00:02 com.greenpoint.android.mc10086.activity:tools
24424 ? 00:00:04 com.greenpoint.android.mc10086.activity:jsengine
oriole:/data/local/tmp # ps -e | grep -i 10086
u0_a273 23701 1223 270840636 832772 do_epoll_wait 0 S com.greenpoint.android.mc10086.activity
u0_a273 23847 1223 15952996 234224 do_freezer_trap 0 S com.greenpoint.android.mc10086.activity:tools
u0_a273 24424 1223 15678692 231676 do_epoll_wait 0 S com.greenpoint.android.mc10086.activity:jsengine
oriole:/data/local/tmp # cat /proc/23701/maps | grep libssl.so
7630a48000-7630a6f000 r--p 00000000 fe:21 181 /apex/com.android.conscrypt/lib64/libssl.so
7630a70000-7630aaf000 r-xp 00028000 fe:21 181 /apex/com.android.conscrypt/lib64/libssl.so
7630ab0000-7630ab4000 r--p 00068000 fe:21 181 /apex/com.android.conscrypt/lib64/libssl.so
7630ab4000-7630ab5000 rw-p 0006c000 fe:21 181 /apex/com.android.conscrypt/lib64/libssl.so
./stackplz -n com.greenpoint.android.mc10086.activity -l /apex/com.android.conscrypt/lib64/libssl.so -w SSL_write[buf:x2:x1,int:x2] --dumphex --color -o tmp2025_03_26_SSL_write_dumphex_mc10086.log
./stackplz -n com.greenpoint.android.mc10086.activity -l /apex/com.android.conscrypt/lib64/libssl.so -w SSL_read[buf:x2:x1,int:x2] --dumphex --color -o tmp2025_03_26_SSL_read_dumphex_mc10086.log
# 通过hook 地址来实现. hook 函数的功能.
./stackplz -n com.greenpoint.android.mc10086.activity -l /apex/com.android.conscrypt/lib64/libssl.so -w 0x2BB98[buf:x2:x0,int:x2] --dumphex --color -o tmp2025_03_26_0x2BB98_SSL_read_dumphex_mc10086.log
# 这里读取的 .ssl_read 没有数据
# 从这里开始 hook SSL_read 的偏移 4F3DC 地址, 开始就有了
./stackplz -n com.greenpoint.android.mc10086.activity -l /apex/com.android.conscrypt/lib64/libssl.so -w 0x4F3DC[buf:x2:x0,int:x2] --dumphex --color -o tmp2025_03_26_4F3DC_SSL_read_dumphex_mc10086.log
0ff0 00 00 00 00 00 00 80 3F 00 00 00 00 00 00 80 40 |.......?.......@|
), arg_1=16384) LR:0x762526dd4c PC:0x7630a973dc SP:0x758ff30b80
[26673|31045|pp.coc.10086.cn] 0x4F3DC(arg_0=0x78238b3618(
0000 A8 1F AB 30 76 00 00 00 18 E6 95 B3 77 00 00 00 |...0v.......w...|
0010 04 03 00 40 00 00 00 00 18 39 99 F3 76 00 00 00 |...@.....9..v...|
0020 18 39 99 F3 76 00 00 00 78 4F A8 30 76 00 00 00 |.9..v...xO.0v...|
0030 F8 EB 8B D3 77 00 00 00 00 00 00 00 00 00 00 00 |....w...........|
0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0050 E8 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
# 这个时候, 你从 SSL_read 时候,他也会给你解析为具体的地址.
./stackplz -n com.greenpoint.android.mc10086.activity -l /apex/com.android.conscrypt/lib64/libssl.so -w SSL_read[buf:x2:x0,int:x2] --dumphex --color -o tmp2025_03_26_4F3DC_SSL_read_dumphex_mc10086.log
), arg_1=16384) LR:0x762526dd4c PC:0x7630a973dc SP:0x74c6dd03c0
[26673|31961|migufun.com/...] SSL_read(arg_0=0x7823956758(
0000 A8 1F AB 30 76 00 00 00 18 00 94 B3 77 00 00 00 |...0v.......w...|
0010 03 03 00 40 00 00 00 00 38 58 A2 F3 76 00 00 00 |...@....8X..v...|
0020 38 58 A2 F3 76 00 00 00 78 4F A8 30 76 00 00 00 |8X..v...xO.0v...|
0d10 B1 E4 B9 90 E6 9C 89 E9 99 90 E5 85 AC E5 8F B8 |................|
0d20 31 16 30 14 06 03 55 04 03 0C 0D 2A 2E 6D 69 67 |1.0...U....*.mig|
0d30 75 66 75 6E 2E 63 6F 6D 17 00 00 83 18 00 00 83 |ufun.com........|
0d40 19 00 00 83 1A 00 00 83 1B 00 00 83 1C 00 00 83 |................|
), arg_1=16384) LR:0x762526dd4c PC:0x7630a973dc SP:0x756fb993c0
[26673|32600|migufun.com/...] SSL_read(arg_0=0x78238e42d8( # 这里都会给你解析为具体的地址.
0000 A8 1F AB 30 76 00 00 00 D8 D7 86 B3 77 00 00 00 |...0v.......w...|
0010 03 03 00 40 77 00 00 00 38 1F 9C F3 76 00 00 00 |...@w...8...v...|
0020 38 1F 9C F3 76 00 00 00 78 4F A8 30 76 00 00 00 |8...v...xO.0v...|
0030 D8 E0 84 D3 77 00 00 00 00 00 00 00 00 00 00 00 |....w...........|
0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0050 E8 03 00 00 37 00 00 00 38 43 A0 93 77 00 00 00 |....7...8C..w...|
0060 00 00 00 00 00 00 00 00 B8 2A 8D D3 77 00 00 00 |.........*..w...|
0070 B8 2A 8D D3 77 00 00 00 98 E6 8B 53 77 00 00 00 |.*..w......Sw...|
0080 00 00 00 00 8B 01 00 00 00 90 01 00 00 00 00 00 |................|
0090 D8 68 A3 43 78 00 00 00 00 00 00 00 00 00 00 00 |.h.Cx...........|
00a0 02 00 00 00 00 AC 02 01 09 01 0B 00 00 00 55 24 |..............U$|
—— 这是以上的分界线.
案例2: hook RegisterNatives动态注册函数追踪
我突然觉得, 从读底层的 jni_internal.cc 的源代码文件, 到 IDA 9 的 C 伪代码, 再到 对应位置的汇编代码, 这样的,这让人对比, 起来,那时相当的直观. 没有任何的问题.
x22, [x9]
x8, [x9, #8 ]
x28,[x9, #0x10] # 这里的 0x10 ,是16进制,实际就是 0, 8, 16, 换算,到 就是 0x10.
![eBPF安卓辅助工具:上 [初级使用]](https://market.nullflag.com/wp-content/themes/sage/themer/assets/images/lazy.png)
![eBPF安卓辅助工具:上 [初级使用]](https://market.nullflag.com/wp-content/themes/sage/themer/assets/images/lazy.png)
![eBPF安卓辅助工具:上 [初级使用]](https://market.nullflag.com/wp-content/themes/sage/themer/assets/images/lazy.png)
现在是 hook libart.so 使用 IDA pro 9, 进行反汇编.
可以根据一些模糊的词汇来下一些断点.
./stackplz -n com.xingin.xhs -l libart.so -w 0x4B8A74[str:x22,str:x8] --tname com.xingin.xhs --reg x28
oriole:/data/local/tmp # ps -e | grep -i 10086
.../local/tmp # ps -e | grep -i 10086
26925 ? 00:00:04 com.greenpoint.android.mc10086.activity:tools
27496 ? 00:03:06 com.greenpoint.android.mc10086.activity:jsengine
.../local/tmp # cat /proc/26925/maps | grep libart.so
7640c00000-7640d6a000 r--p 00000000 fe:0d 84 /apex/com.android.art/lib64/libart.so
7640e00000-7641442000 rwxp 00200000 fe:0d 84 /apex/com.android.art/lib64/libart.so
7641600000-7641611000 r--p 00a00000 fe:0d 84 /apex/com.android.art/lib64/libart.so
7641810000-7641814000 rw-p 00a10000 fe:0d 84 /apex/com.android.art/lib64/libart.so
stackplz没有提供直接对函数执行结束后的hook功能,不过目前引入了--dumpret
通过该选项,可以解析出给定符号关键词的返回指令偏移,使用该偏移快速下uprobe断点同样能达到预期效果
.../local/tmp # ./stackplz -n com.greenpoint.android.mc10086.activity -l libart.so -w GetStringUTFChars --dumpret
warn, no running process of com.greenpoint.android.mc10086.activity
FindRet for GetStringUTFChars failed,
sym:_ZN3art12_GLOBAL__N_18CheckJNI17GetStringUTFCharsEP7_JNIEnvP8_jstringPh.__uniq.99033978352804627313491551960229047428.llvm.14769753146406819462 offset:0x528704
FindRet for GetStringUTFChars -> [0x6bc2d8] sym:_ZN3art3JNIILb0EE17GetStringUTFCharsEP7_JNIEnvP8_jstringPh offset:0x6bc0b0
FindRet for GetStringUTFChars -> [0x70c4d0] sym:_ZN3art3JNIILb1EE17GetStringUTFCharsEP7_JNIEnvP8_jstringPh offset:0x70c2a8
# 地址的话,两个都是, 反正都是两个.
.../local/tmp # ./stackplz -n com.greenpoint.android.mc10086.activity -l libart.so -w 0x6bc2d8[str] --getoff
.../local/tmp # ./stackplz -n com.greenpoint.android.mc10086.activity -l libart.so -w 0x70c4d0[str] --getoff
![eBPF安卓辅助工具:上 [初级使用]](https://market.nullflag.com/wp-content/themes/sage/themer/assets/images/lazy.png)
![eBPF安卓辅助工具:上 [初级使用]](https://market.nullflag.com/wp-content/themes/sage/themer/assets/images/lazy.png)
![eBPF安卓辅助工具:上 [初级使用]](https://market.nullflag.com/wp-content/themes/sage/themer/assets/images/lazy.png)
![eBPF安卓辅助工具:上 [初级使用]](https://market.nullflag.com/wp-content/themes/sage/themer/assets/images/lazy.png)
![eBPF安卓辅助工具:上 [初级使用]](https://market.nullflag.com/wp-content/themes/sage/themer/assets/images/lazy.png)
学习一门技术肯定从复现开始. 最开始一定是深究他的原理.
![eBPF安卓辅助工具:上 [初级使用]](https://market.nullflag.com/wp-content/themes/sage/themer/assets/images/lazy.png)
最后定位在 0x54B470.
./stackplz -n com.greenpoint.android.mc10086.activity -l libart.so -w 0x54B470[str:x23,str:x8] --reg x26
[5328|5486|tbs_preinit] 0x54B470(arg_0=0x754db71f91(clearAllWithKeepingSpace), arg_1=0x754db7170e(()V)) LR:0x764114b50c PC:0x764114b470 SP:0x757fd633b0, RegsInfo:
x26(0x754db1bcd0 libmmkv.so + 0x13cd0)
[5328|5634|mriver-init] 0x54B470(arg_0=0x7500ef7c8d(nativeGetPagerStats), arg_1=0x7500ef7ca1((Lcom/alibaba/sqlcrypto/sqlite/SQLiteDebug$PagerStats;)V)) LR:0x764114b3bc PC:0x764114b470 SP:0x7559266f20, RegsInfo:
x26(0x7500deba88 libdatabase_sqlcrypto.so + 0x91a88)
[5328|5328|c10086.activity] 0x54B470(arg_0=0x759e5dcc
./stackplz -n com.greenpoint.android.mc10086.activity -l libart.so -w 0x54B470[str:x23,str:x8] --reg x26 --color -o tmp2025_03_26_for_libexec.so_break_frida.log
open64[str.f0,int] -f w:/data
.../local/tmp # ./stackplz -n com.greenpoint.android.mc10086.activity -l libart.so -w 0x54B470[str:x23,str:x8] --reg x26 --color -o tmp2025_03_26_for_libexec.so_break_frida.log
warn, no running process of com.greenpoint.android.mc10086.activity
hook uprobe, count:1
idx:0 [/apex/com.android.art/lib64/libart.so + 0x54b470] str:x23,str:x8
ConfigMap{stackplz_pid=7978,thread_whitelist=0}
uid => whitelist:[10273];blacklist:[]
pid => whitelist:[];blacklist:[]
tid => whitelist:[];blacklist:[]
start 2 modules
[8040|8040|c10086.activity] 0x54B470(arg_0=0x790b22ea90(), arg_1=0x790b22ecc0()) LR:0x764114b3bc PC:0x764114b470 SP:0x7fcc8a61f0, RegsInfo:
x26(0x78dde533a0 libandroid_runtime.so + 0x1fa3a0)
[8040|8040|c10086.activity] 0x54B470(arg_0=0x790b22f410(nativeSpecializeAppProcess), arg_1=0x790b22f2b0((II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)V)) LR:0x764114b50c PC:0x764114b470 SP:0x7fcc8a61f0, RegsInfo:
x26(0x78dde54170 libandroid_runtime.so + 0x1fb170)
[8040|8040|c10086.activity] 0x54B470(arg_0=0x790b22f710(nativeForkSystemServer), arg_1=0x790b22f4f8((II[II[[IJJ)I)) LR:0x764114b50c PC:0x764114b470 SP:0x7fcc8a61f0, RegsInfo:
x26(0x78dde53850 libandroid_runtime.so + 0x1fa850)
[8040|8040|c10086.activity] 0x54B470(arg_0=0x762fd898c6(native_tagSocketFd), arg_1=0x762fd89894((Ljava/io/FileDescriptor;II)I)) LR:0x764114b3bc PC:0x764114b470 SP:0x7fcc8a5cc0, RegsInfo:
x26(0x762fd8d594 libframework-connectivity-tiramisu-jni.so + 0x4594)
[8040|8040|c10086.activity] 0x54B470(arg_0=0x762fd89830(native_untagSocketFd), arg_1=0x762fd89814((Ljava/io/FileDescriptor;)I)) LR:0x764114b50c PC:0x764114b470 SP:0x7fcc8a5cc0, RegsInfo:
x26(0x762fd8d5d8 libframework-connectivity-tiramisu-jni.so + 0x45d8)
[8040|8040|c10086.activity] 0x54B470(arg_0=0x75c15a2848(l), arg_1=0x75c15d6c2f((Landroid/app/Application;Ljava/lang/String;)Z)) LR:0x764114b3bc PC:0x764114b470 SP:0x7fcc8a5600, RegsInfo:
x26(0x75c1501f4c UNKNOWN)
[8040|8040|c10086.activity] 0x54B470(arg_0=0x75c15a2a9a(r), arg_1=0x75c15d6c2f((Landroid/app/Application;Ljava/lang/String;)Z)) LR:0x764114b50c PC:0x764114b470 SP:0x7fcc8a5600, RegsInfo:
x26(0x75c1503d24 UNKNOWN)
[8040|8040|c10086.activity] 0x54B470(arg_0=0x75c15d6c8c(ra), arg_1=0x75c15d6c2f((Landroid/app/Application;Ljava/lang/String;)Z)) LR:0x764114b50c PC:0x764114b470 SP:0x7fcc8a5600, RegsInfo:
x26(0x75c150433c UNKNOWN)
[8040|8040|c10086.activity] 0x54B470(arg_0=0x75c15d6c90(b2b), arg_1=0x75c15d6c98(([BI)[B)) LR:0x764114b50c PC:0x764114b470 SP:0x7fcc8a5600, RegsInfo:
x26(0x75c150497c UNKNOWN)
[8040|8040|c10086.activity] 0x54B470(arg_0=0x75c15a284a(m), arg_1=0x75c15d6ca0((Ljava/lang/String;I)V)) LR:0x764114b50c PC:0x764114b470 SP:0x7fcc8a5600, RegsInfo:
x26(0x75c15049e8 UNKNOWN)
[8040|8040|c10086.activity] 0x54B470(arg_0=0x75c15d6cb8(sa), arg_1=0x75c15d6cbb((Ljava/lang/String;Ljava/lang/String;)V)) LR:0x764114b50c PC:0x764114b470 SP:0x7fcc8a5600, RegsInfo:
x26(0x75c15049ec UNKNOWN)
[8040|8040|c10086.activity] 0x54B470(arg_0=0x75c15d6ce4(al), arg_1=0x75c15d6ce7((Ljava/lang/ClassLoader;Landroid/content/pm/ApplicationInfo;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/ClassLoader;)) LR:0x764114b50c PC:0x764114b470 SP:0x7fcc8a5600, RegsInfo:
x26(0x75c1504a48 UNKNOWN)
[8040|8040|c10086.activity] 0x54B470(arg_0=0x75c15a284c(i), arg_1=0x75c15d6d60((I)V)) LR:0x764114b3bc PC:0x764114b470 SP:0x7fcc8a5600, RegsInfo:
x26(0x75c15256bc UNKNOWN)
[8040|8040|c10086.activity] 0x54B470(arg_0=0x75c15d3b20(println_native), arg_1=0x75c15d3b2f((IILjava/lang/String;Ljava/lang/String;)I)) LR:0x764114b3bc PC:0x764114b470 SP:0x7fcc8a5af0, RegsInfo:
x26(0x75c14deae4 UNKNOWN)
# 上面没有排查出来就被终止了.
为什么从新找, 不一样呢?
说明他的 registerNative的方法名, 方法签名, 都打印出来了. 地址在 哪个so的地址都有. 方法名, 方法签名, 有些有解析, 有些没有解析. 有的 从哪个so 加了多少偏移 , 位置都有, 对把, 位置都有, 都是比较直观的, 比较漂亮, 为什么之前的那个不行,🙈,
![eBPF安卓辅助工具:上 [初级使用]](https://market.nullflag.com/wp-content/themes/sage/themer/assets/images/lazy.png)
54b468, 这个位置是对的, 为什么, method name , 为什么实际操作的时候找不到呢?
![eBPF安卓辅助工具:上 [初级使用]](https://market.nullflag.com/wp-content/themes/sage/themer/assets/images/lazy.png)
看到了, 找错地方了. 在 string的位置, 找到了 method name 后, 应该使用 第二个 xref, 引用, 这样就不会迷路了.
![eBPF安卓辅助工具:上 [初级使用]](https://market.nullflag.com/wp-content/themes/sage/themer/assets/images/lazy.png)
![eBPF安卓辅助工具:上 [初级使用]](https://market.nullflag.com/wp-content/themes/sage/themer/assets/images/lazy.png)
![eBPF安卓辅助工具:上 [初级使用]](https://market.nullflag.com/wp-content/themes/sage/themer/assets/images/lazy.png)
![eBPF安卓辅助工具:上 [初级使用]](https://market.nullflag.com/wp-content/themes/sage/themer/assets/images/lazy.png)
不过印度的媒介状况与国内的不同之处在于,他们的纸媒是成熟的商业机构媒体,这种差异具体到报纸版面上显而易见。
我观察了一份印度当地颇有影响力的商业报纸《The Times of India》,不同于国内报纸头版一定会刊登要闻,这份商业报纸的前两版分别是地产广告和商场促销广告。
印度用户在消费这些内容时大多需要付费订阅,因此UC News与所有印度头部的媒体机构达成了版权合作,印度的用户便可在UC News上免费获取内容。
印度自媒体,和国内不一样
除了通过与头部机构媒体合作获取内容外,他们也借助“UC we-media”内容创作者平台引入更多自媒体入驻。
“在内容供给上,可能70%—80%都是机构媒体,机构媒体生产的内容我们叫做OGC,剩下的一两成都是自媒体,也就是有创作能力的PGC。但是目前还没有UGC……”席宇解释说。
在印度,“自媒体”的含义比国内更为严格一些,UGC尚且不在此列。在他们看来真正的UGC是抖音、快手、Vmate里这种纯记录生活的内容。而UC News平台目前将自己定义为一个PGC的内容平台,平台上的作者即便是个人生产,他们也是专业且有经验的内容生产者。
去年年初,UC宣布投资3000万美元用于内容分发平台,UC News的内容激励机制吸引了越来越多优质内容创作者。
Inna Khosla 就是UC平台上的优秀自媒体作者之一。
Inna早期是一名音乐老师,后离职进入当地的广电媒体做了10年制片,先后在印度当地多家知名媒体工作过,可以说是有丰富经验的媒体人了。
在印度这个以男性为主的社会中,女性的社会地位虽然在逐渐提高,但只要结婚生了孩子,就无法继续在工作岗位上工作,也不再有收入来源,全依靠夫家的支持生活。
比起之前在家带孩子,如今的Inna在“UC we-media”平台上开设了自己的账号,闲暇之余写写明星穿搭、美容美妆等以时尚为主的内容。她还特别高产,几乎每天都会更新,且每天至少要更新两篇文章,生怕自己错过了当天的热点。
努力的Inna老师也因此依靠UC的平台分成获得了不少收入,用她自己的话说“实现了经济的独立”,却并不透露具体的数额。
当然平台的分成与文章的质量、阅读量、点击量等多维度的标准有关,有时连阅读量也起伏不定,但在家写字就能让她实现经济独立的感觉可以说很美妙了。
不一样的国度,一样的阿里文化
UC印度办公室的构造非常简单,几排桌椅间充满了埋头工作的员工,他们中大多数是印度当地员工,但每扫几排也能看到熟悉的中国面孔。
经过走廊的时候,正好撞见办公室里的电视上在播放马云的演讲。就连会议室的背景墙上也写上“客户价值=客户体验*客户利益”,足以见即使在印度的本土化做得足够深入,阿里文化在印度公司却丝毫未消减。
UC印方的工作人员告诉我们,为了帮助UC做好印度的本土化,他们的员工中有80%以上来自印度当地。
且自从2016年发布UC News转向内容平台以来,他们的内容团队里吸纳了许多曾在印度传统媒体工作过的成熟编辑和制作人。
“我们要求员工要有相对专业的背景,或者他大学学这方面的专业、或者毕业后的工作在专业的机构媒体或者成熟的内容团队。而我们核心的编辑,也是来自最头部的机构媒体。”UC国际信息流内容运营负责人陈俊杰告诉我们。
这些曾经在印度当地媒体工作多年的成熟编辑比中国员工更了解印度用户喜欢什么样的内容,而UC News在印度的月活用户在上线9个月的时候就已突破了8000万,足见其本土化程度短期内就已得到认可。
也难怪我在印度探访朋友Frank的时候,他也告诉我“UC在印度本土扎根很深入”。
印度人口众多,国情复杂。且不说有佛教、印度教和伊斯兰教等复杂的宗教历史,印度分布着100多种民族,南北部人民的语言习惯也差异极大。
UC的工作人员向我们介绍说,印度北部有40%的人习惯使用印地语,南部的语言更是繁复多样。为此,他们也费了不少心思在印度本土语言的调研上。
“我们调研方言在当地的情况发现印地语在本地的渗透率会高于英语,而且北部使用印地语的频率会高于南部。”UC浏览器国际业务负责人王文祥介绍说,南部小语种杂多且口音较重,因此带来的文化区隔也远大于北部,南部会有更小众的文化圈存在。
朋友Frank告诉我,目前在印度小语种的内容市场还是蓝海。因此一旦能打破语言壁垒,整个印度的内容市场将能全面打开。
UC印度的工作人员口中常常会提到一个词——“Glocal”,也就是“Global”和“Local”的融合。这是UC出海战略的核心:全球化思维和本地化经营,两者深度融合才有了UC今天在印度取得的成绩。
在UC事业群总裁朱顺炎看来:全球化作为一种理念势必要在最初想清楚,这决定了拿什么样的标准去要求产品,也就是产品的格局;而本土化作为一种经营策略也不能单单是招一群印度员工来为产品服务,更要从文化深处与他们互相融合。
当然,于UC而言,在印度做内容生态是一件非常复杂且艰难的事情。“生态这个东西,和你原来做一个工具很不一样,生态是各个环节的参与者他都要能欣欣向荣,这个生态才能繁荣。在我们的生态里面,比如说商业化、比如说我们对于内容和本地机构的合作,它是不是足够的深入,这个方面我觉得有待提高。”朱顺炎也直言在印度做内容生态的挑战。
中国企业出海,机遇与挑战并存
中国的互联网产品或企业出海的方式大抵可分为两种:一种是通过投资当地的互联网产品、互相借力以深入当地市场;另一种则是带着中国基因的互联网产品,通过本土化的方式在海外扎根。
当然从根本上说,本土产品的出海甚至是国际化都是异常难走的一条路。即便如此,这条出海的大船上依然有不少中国企业前赴后继。
中国头号社交软件微信在登陆印度市场遭遇Whatsapp这个劲敌之后,选择花1.75亿美元投资印度本土社交软件Hike Message,扶持它成为“印度微信”。
支付宝在切入印度市场时也选择了前者,2015年投资印度本土移动支付平台Paytm,被称为“印度支付宝”。
可以说通过投入资金和技术,与印度本土的互联网公司形成互补之势,大概是中国企业出海最为高效的一种方式。
相比于稳妥的投资出海,选择扎根本土市场,需要面临相当长的本土化进程以及风云莫测的海外市场。除非护城河异常坚固,否则在出海途中极容易溃败而返。
尽管UC借助工具产品UC Browers打入印度市场并超越了同赛道的美国竞品,但谷歌Chrome通过手机预装,在印度的增长也十分迅猛。UC News借助内容生态深入印度本土,在用户黏性和忠诚度上再度筑高了壁垒。
“同时也有挑战,我们发现印度市场产品形态变化很快,中国公司加大投入的同时,像Facebook的Instagram和whatsapp这样的美国公司都非常强劲。UC然是面临一个更激烈的竞争局面,所以机会很大,挑战也很大。”朱顺炎说。
早在马云1999年创立阿里巴巴的时候,就曾说过“我们的竞争对手不在中国,而在美国的硅谷”;过了近20年,这句话看起来越来越成为现实了。
