Skip to content

Commit 2ea9925

Browse files
committed
变更硬件断点默认行为,发布新版本
1 parent dfb40fb commit 2ea9925

File tree

8 files changed

+49
-40
lines changed

8 files changed

+49
-40
lines changed

README.md

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ stackplz的所有可用选项,可以通过`./stackplz --help`查看
7171

7272
| 选项 | 默认值 | 说明 |
7373
| :- | :-: | :- |
74+
| --pid | | 目标进程pid,与--brk-lib搭配使用计算断点地址 |
7475
| --brk | | 要下断的地址 |
7576
| --brk-len | 4 | 断点长度 |
7677
| --brk-lib | | 目标库,使用该选项时--brk为相对偏移 |
@@ -128,7 +129,7 @@ stackplz的所有可用选项,可以通过`./stackplz --help`查看
128129
- 配置文件具体使用方式请查看[配置文件文档](./docs/CONFIG.md)
129130
- `--full-tname` 默认对于一些高频调用syscall的系统线程进行了屏蔽,启用该选项后将解除屏蔽
130131
- `-l/--lib` 动态库名或者动态库完整路径,配合`-w/--point`选项使用
131-
- `-o/--out` 日志文件名,默认`stackplz_tmp.log`
132+
- `-o/--out` 日志文件名,默认不生成日志文件
132133
- `--dump` 即dump模式,hook获取到的数据不会被解析,仅保存到单个文件
133134
- `--parse` 即针对dump得到的文件进行解析,可能比较耗时,可能存在bug
134135
- `--stack-size` 堆栈大小,默认8192字节,基本够用,最大65528
@@ -168,15 +169,7 @@ stackplz的所有可用选项,可以通过`./stackplz --help`查看
168169

169170
![](./images/Snipaste_2023-07-22_21-21-33.png)
170171

171-
3.3 通过**指定包名**,对`libnative-lib.so``_Z5func1v`符号处下执行断点,并打印堆栈
172-
173-
```bash
174-
./stackplz --brk-pid `pidof com.sfx.ebpf` --brk 0xf3a4:x --brk-lib libnative-lib.so --stack
175-
```
176-
177-
![](./images/Snipaste_2023-09-08_17-08-42.png)
178-
179-
3.4 在命中uprobe hook时发送信号
172+
3.3 在命中uprobe hook时发送信号
180173

181174
有时候希望在经过特定点位的时候停止进程,以便于dump内存,那么可以使用`--kill`来发送信号,示例:
182175

@@ -193,34 +186,34 @@ kill -SIGCONT 4326
193186

194187
v3.0.0版本起,可以在终端输入c后回车恢复进程运行
195188

196-
3.5 **硬件断点**示例如下,支持的断点类型:`r,w,rw,x`
189+
3.4 **硬件断点**示例如下,支持的断点类型:`r,w,rw,x`
197190

198191
pid + 绝对地址
199192

200193
```bash
201-
./stackplz --brk-pid `pidof com.sfx.ebpf` --brk 0x70ddfd63f0:x --stack
194+
./stackplz --pid `pidof com.sfx.ebpf` --brk 0x70ddfd63f0:x --stack
202195
```
203196

204197
pid + 偏移 + 库文件
205198

206199
```bash
207-
./stackplz --brk-pid `pidof com.sfx.ebpf` --brk 0xf3a4:x --brk-lib libnative-lib.so --stack
200+
./stackplz --pid `pidof com.sfx.ebpf` --brk 0xf3a4:x --brk-lib libnative-lib.so --stack
208201
```
209202

203+
![](./images/Snipaste_2024-03-04_21-51-12.png)
204+
210205
对内核中的函数下硬件断点:
211206

212207
**!!!注意,内核函数通常触发非常频繁,该操作可能导致设备重启,请谨慎使用,原因不明**
213208

214209
```bash
215210
echo 1 > /proc/sys/kernel/kptr_restrict
216211
cat /proc/kallsyms | grep "T sys_"
217-
./stackplz --brk 0xffffff93c5beb634:x --brk-pid `pidof com.sfx.ebpf` --stack
218-
./stackplz --brk 0xffffffc0003654dc:x --brk-pid `pidof com.sfx.ebpf` --regs
212+
./stackplz --brk 0xffffff93c5beb634:x --pid `pidof com.sfx.ebpf` --stack
213+
./stackplz --brk 0xffffffc0003654dc:x --pid `pidof com.sfx.ebpf` --regs
219214
```
220215

221-
某些时候确信数据被访问了,但是上面的命令还是没有输出,请尝试省略`--brk-pid`选项,即使用默认值`-1`
222-
223-
3.6 以寄存器的值作为大小读取数据、或者指定大小
216+
3.5 以寄存器的值作为大小读取数据、或者指定大小
224217

225218
```bash
226219
./stackplz --name com.sfx.ebpf -w write[int,buf:x2,int]
@@ -278,7 +271,7 @@ cat /proc/kallsyms | grep "T sys_"
278271
./stackplz -n com.termux -w 0x9D150[int,buf:x2,int]0x9D164 --dumphex --color
279272
```
280273
281-
3.8 按分组批量追踪进程
274+
3.6 按分组批量追踪进程
282275
283276
追踪全部APP类型的进程,但是排除一个特定的uid:
284277
@@ -294,7 +287,7 @@ cat /proc/kallsyms | grep "T sys_"
294287
295288
可选的进程分组:root system shell app iso
296289
297-
3.9 按分组批量追踪syscall
290+
3.7 按分组批量追踪syscall
298291
299292
```bash
300293
./stackplz -n com.xingin.xhs -s %file,%net --no-syscall openat,recvfrom
@@ -312,7 +305,7 @@ cat /proc/kallsyms | grep "T sys_"
312305
313306
具体分组情况请查看[Parse_SyscallNames](./user/config/config_module.go)
314307
315-
3.10 应用过滤规则
308+
3.8 应用过滤规则
316309
317310
黑白名单:
318311
@@ -357,9 +350,9 @@ LR比较,需要提前计算用于比较的值:
357350
- --syscall openat
358351
- 特别的,指定为`all`表示追踪全部syscall
359352
- --syscall all
360-
- **特别说明**很多结果是`0xffffff9c`这样的结果,其实是`int`,但是目前没有专门转换
353+
- **特别说明**如果期望将`0xffffff9c`这样的结果输出为负数,请明确指定类型为`int`
361354
- 注意,本项目中syscall的返回值通常是**errno**,与libc的函数返回结果不一定一致
362-
- `--dumphex`表示将数据打印为hexdump,否则将记录为`ascii + hex`的形式
355+
- `--dumphex`表示将数据打印为hexdump,否则将记录为`ascii + hex`的形式,另外可添加`--color`选项
363356
- 输出到日志文件添加`-o/--out tmp.log`,只输出到日志,不输出到终端再加一个`--quiet`即可
364357
365358
**注意**,默认屏蔽下列线程,原因是它们属于渲染或后台相关的线程,会触发大量的syscall调用
@@ -409,7 +402,7 @@ LR比较,需要提前计算用于比较的值:
409402
./stackplz -n com.starbucks.cn -b 32 --syscall all -o tmp.log
410403
```
411404
412-
一味增大缓冲区大小也可能带来新的问题,比如分配失败,这个时候建议尽可能清理正在运行的进程
405+
增大缓冲区大小也可能带来新的问题,比如分配失败,这个时候建议尽可能清理正在运行的进程
413406
414407
> failed to create perf ring for CPU 0: can't mmap: cannot allocate memory
415408
@@ -435,10 +428,6 @@ coral:/data/local/tmp # readelf -s /apex/com.android.runtime/lib64/bionic/libc.s
435428
6853: 00000000000b9f00 32 GNU_IFUNC GLOBAL DEFAULT 14 strchrnul
436429
```
437430
438-
如图,可以看到直接调用了`__strchr_aarch64`而不是经过`strchr`再去调用`__strchr_aarch64`
439-
440-
![](./images/Snipaste_2022-11-13_14-19-38.png)
441-
442431
# 文章
443432
444433
个人碎碎念太多,有关stackplz文章就不同步到本项目了,请移步博客查看:

cli/cmd/root.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -285,10 +285,14 @@ func persistentPreRunEFunc(command *cobra.Command, args []string) error {
285285
// 4. watch breakpoint
286286
var brk_base uint64 = 0x0
287287
if gconfig.BrkLib != "" {
288-
if gconfig.BrkPid <= 0 {
289-
return errors.New("must set --brk-pid when use --brk-lib option")
288+
if gconfig.Pid == "" {
289+
return errors.New("must set --pid when use --brk-lib option")
290290
}
291-
lib_info, err := event.FindLibInMaps(uint32(gconfig.BrkPid), gconfig.BrkLib)
291+
value, err := strconv.ParseUint(gconfig.Pid, 10, 32)
292+
if err != nil {
293+
panic(err)
294+
}
295+
lib_info, err := event.FindLibInMaps(uint32(value), gconfig.BrkLib)
292296
if err != nil {
293297
return err
294298
}
@@ -612,8 +616,8 @@ func init() {
612616
rootCmd.PersistentFlags().StringVar(&gconfig.RpcPath, "rpc-path", "127.0.0.1:41718", "rpc path, default 127.0.0.1:41718")
613617
// 硬件断点设定
614618
rootCmd.PersistentFlags().StringVar(&gconfig.BrkAddr, "brk", "", "set hardware breakpoint address")
615-
rootCmd.PersistentFlags().IntVar(&gconfig.BrkPid, "brk-pid", -1, "set hardware breakpoint pid")
616-
rootCmd.PersistentFlags().StringVar(&gconfig.BrkLib, "brk-lib", "", "as library base address")
619+
rootCmd.PersistentFlags().IntVar(&gconfig.BrkPid, "brk-pid", -1, "set hardware breakpoint pid, just keep default")
620+
rootCmd.PersistentFlags().StringVar(&gconfig.BrkLib, "brk-lib", "", "as library base address, work with -p/--pid option")
617621
rootCmd.PersistentFlags().Uint64Var(&gconfig.BrkLen, "brk-len", 4, "hardware breakpoint length, default 4, support [1, 8]")
618622
// 缓冲区大小设定 单位M
619623
rootCmd.PersistentFlags().Uint32VarP(&gconfig.Buffer, "buffer", "b", 8, "perf cache buffer size, default 8M")
@@ -629,7 +633,7 @@ func init() {
629633
rootCmd.PersistentFlags().BoolVarP(&gconfig.Quiet, "quiet", "q", false, "wont logging to terminal when used")
630634
rootCmd.PersistentFlags().BoolVar(&gconfig.Color, "color", false, "enable color for log file")
631635
rootCmd.PersistentFlags().BoolVarP(&gconfig.FmtJson, "json", "j", false, "log event as json format")
632-
rootCmd.PersistentFlags().StringVarP(&gconfig.LogFile, "out", "o", "stackplz_tmp.log", "save the log to file")
636+
rootCmd.PersistentFlags().StringVarP(&gconfig.LogFile, "out", "o", "", "save the log to file")
633637
// 适合收集大量数据 减少数据丢失
634638
rootCmd.PersistentFlags().StringVar(&gconfig.DumpFile, "dump", "", "save perf data to file")
635639
rootCmd.PersistentFlags().StringVar(&gconfig.ParseFile, "parse", "", "parse perf data as json or readable format")

frida_hw_brk.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ async function SetHWBrk(brk_addr, brk_type) {
77
let size_len = 4;
88

99
let brk_options = {
10-
brk_pid: Process.id,
10+
// brk_pid: Process.id,
11+
brk_pid: -1,
1112
brk_len: 4,
1213
brk_type: brk_type,
1314
brk_addr: brk_addr,
-247 KB
Binary file not shown.
-491 KB
Binary file not shown.
158 KB
Loading

user/event/event_brk.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,11 @@ func (this *BrkEvent) ParseEvent() (IEventStruct, error) {
5656
if err := this.ParseContext(); err != nil {
5757
panic(fmt.Sprintf("SyscallEvent.ParseContext() err:%v", err))
5858
}
59-
this.Check()
59+
if !this.Check() {
60+
// 当明确设置 --brk-pid 的时候
61+
// 仅输出事件 pid 与 --brk-pid 一致的事件
62+
return nil, nil
63+
}
6064
return this, nil
6165
}
6266

@@ -104,10 +108,19 @@ func (this *BrkEvent) ParseContextStack() {
104108
if err != nil {
105109
panic(fmt.Sprintf("UnwindStack ParseContext failed, err:%v", err))
106110
}
107-
// 立刻获取堆栈信息 对于某些hook点前后可能导致maps发生变化的 堆栈可能不准确
108-
// 这里后续可以调整为只dlopen一次 拿到要调用函数的handle 不要重复dlopen
111+
if this.mconf.ManualStack {
112+
CacheMaps(this.Pid)
113+
maps_helper.SetLogger(this.logger)
114+
info, err := maps_helper.GetStack(this.GetPid(), this.UnwindBuffer)
115+
if err != nil {
116+
this.logger.Printf("Error when GetStack:%v", err)
117+
} else {
118+
this.Stackinfo = info
119+
}
120+
return
121+
}
109122
content, err := util.ReadMapsByPid(this.GetPid())
110-
if err != nil || this.mconf.ManualStack {
123+
if err != nil {
111124
// 直接读取 maps 失败 那么从 mmap2 事件中获取
112125
// 根据测试结果 有这样的情况 -> 即 fork 产生的子进程 那么应该查找其父进程 mmap2 事件
113126
maps_helper.SetLogger(this.logger)

user/rpc/rpc.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ type BrkOptionsRaw struct {
4949
}
5050

5151
func BrkIt(opts *BrkOptions) {
52-
event.CacheMaps(uint32(opts.BrkPid))
52+
if opts.BrkPid != -1 {
53+
event.CacheMaps(uint32(opts.BrkPid))
54+
}
5355
mod := module.GetModuleByName(module.MODULE_NAME_BRK)
5456
var mconfig = config.NewModuleConfig()
5557
mconfig.Debug = Gconfig.Debug

0 commit comments

Comments
 (0)