Skip to content

Commit 713e242

Browse files
committedOct 27, 2024·
add some true blogs & mv resources
1 parent 6ea997d commit 713e242

23 files changed

+545
-12
lines changed
 

‎source/_posts/dasx0rays2024/WhereIsMySauce.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ updated: 2024/10/23 19:03:00
55
tags:
66
- tricks
77
sticky: 90
8+
thumbnail: /assets/dasx0rays2024/debuginfod.png
89
---
910
<!-- excerpt -->
1011

File renamed without changes.

‎source/_posts/trueblog/debuginfod.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ updated: 2024/7/25 12:34:56
55
tags:
66
- tricks
77
- not-ctf
8-
thumbnail: /assets/gdbWithCode.png
8+
thumbnail: /assets/trueblog/gdbWithCode.png
99
---
1010

1111
<!--excerpt-->
1212

1313
自从做pwn题以来,一直有一个奇怪的现象,当我调试程序时,遇到libc中的函数就会显示源代码,
1414
但是一旦patchelf了以后Pwndbg就无法显示源代码了
1515

16-
![source code shown](/assets/gdbWithCode.png)
16+
![source code shown](/assets/trueblog/gdbWithCode.png)
1717

1818
{% note %}
1919
注:笔者使用Arch Linux
@@ -47,7 +47,7 @@ ln -s "$LIBC/.debug" .
4747
同时`info share`显示libc.so.6的调试符号是缺失的,哪怕是执行了`set debug-file-directory $LIBC/.debug`
4848
亦或是`set debug-file-directory $LIBC/.debug/.build-id`之后
4949

50-
![no debugging symbols](./assets/gdbWithoutDbg.png)
50+
![no debugging symbols](/assets/trueblog/gdbWithoutDbg.png)
5151

5252
{% note info fa-circle-arrow-right %}
5353
事实上`set debug-file-directory $LIBC/.debug`是可以加载调试符号的,但***必须***在程序启动前或附加上去前设置
@@ -74,7 +74,7 @@ ln -s "$LIBC/.debug" .
7474

7575
Pwndbg已经自动在~/.gdbinit中写入`set debuginfod enabled on`,此时调试题目,gdb就会自动根据patch的libc下载debuginfo了
7676

77-
![now we have debugging symbols](./assets/gdbWithDbg.png)
77+
![now we have debugging symbols](/assets/trueblog/gdbWithDbg.png)
7878

7979
有读者可能会问:源码呢?源码还是没下成功啊?很遗憾,ubuntu的debuginfo并不提供源码,
8080
以下话转自[官网](https://ubuntu.com/server/docs/about-debuginfod)

‎source/_posts/trueblog/gdbInMsys.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ tags:
66
- tricks
77
- Windows
88
- not-ctf
9-
thumbnail: /assets/success.png
9+
thumbnail: /assets/trueblog/success.png
1010
---
1111

1212
<!--excerpt-->
@@ -30,13 +30,13 @@ pacman -S mingw-w64-ucrt-x86_64-python-pygments
3030

3131
然后又出bug了:
3232

33-
![锟斤拷烫烫烫](/assets/gbk.png)
33+
![锟斤拷烫烫烫](/assets/trueblog/gbk.png)
3434

3535
怎么锟斤拷都出来了??于是我就设置了gdb的选项`host-charset`, `target-charset`, `target-wide-charset`,
3636
`charset`都为`UTF-8`,还是不行,看到有人说,gdb应该读取外部的LANG环境变量来决定编码,但在Windows平台,
3737
还是要过一遍Windows-console,编码又变回了GBK,必须在运行gdb的时候手动把代码页设置为65001,于是我也跟着做了,
3838
gdb显示no context,没办法,我只能将Windows全局设为了UTF-8,终于解决了问题
3939

40-
![configuration](/assets/conf.png)
40+
![configuration](/assets/trueblog/conf.png)
4141

42-
![sucess](/assets/success.png)
42+
![sucess](/assets/trueblog/success.png)

‎source/_posts/trueblog/ifuncInPuts.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ tags:
66
- libc-hook
77
- not-ctf
88
- got-hijack
9-
thumbnail: /assets/disasm.png
9+
thumbnail: /assets/trueblog/disasm.png
1010
---
1111

1212
<!--excerpt-->
@@ -24,8 +24,8 @@ thumbnail: /assets/disasm.png
2424

2525
于是我就在本地调了一下,也是类似有个跳表存在,不过Arch的libc不能改这一项(因为开了Full RELRO)
2626

27-
![what is ABS?](/assets/disasm.png)
28-
![can not write](/assets/vmmap.png)
27+
![what is ABS?](/assets/trueblog/disasm.png)
28+
![can not write](/assets/trueblog/vmmap.png)
2929

3030
可以看到出现跳表的位置对应源码是在`strlen`的位置,在[elixir](https://elixir.bootlin.com/glibc/glibc-2.38/source/libio/ioputs.c#L48)中,
3131
原始的`puts`函数是这样的:
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
title: 看电影的学问——IMAX & Dolby Cinema
3+
date: 2024/10/18 17:41:00
4+
updated: 2024/10/18 17:41:00
5+
tags:
6+
- not-ctf
7+
thumbnail: /assets/trueblog/IMAX_blue_logo.svg
8+
---
9+
<!-- excerpt -->
10+
11+
暑假前看了流浪地球2,就算在我家的小电视上都能看出冲击感。之后在B站看深度解读的时候,
12+
看到评论区里都在讨论相比起球1的进步以及自己已经去电影院看了多少次了。
13+
这不禁让我也有了去电影院二刷的想法,当时我就随便就近挑了一家IMAX,准备寒假排片的时候去看。
14+
15+
随后,流浪地球2 3D版预告上线了。看得早不如看得巧,计划计划我就打算和好友一起去看。
16+
在官方海报中,写了有IMAX、杜比影院、CINITY和CGS中国巨幕。所有的IMAX影院都一样...吗?
17+
18+
<img src="/assets/trueblog/wandering.jpg" height="30%" width="30%">
19+
20+
## 影院等级
21+
22+
偶然看到了有视频推荐IMAX一代。一代?还有二代吗?继续查下去,具体来说,IMAX出过好几代,
23+
先是胶卷介质(国内没有),后来又出现了数字介质,分为GT, COLA, Laser XT, 氙灯等四个版本,
24+
这四个版本沿顺序搭建成本逐渐降低,效果也逐渐变差,氙灯在全国最多,但是其亮度看3D版本的电影完全不够;
25+
GT在全国只有寥寥几家,效果也最好。在杭州,最好的IMAX只有COLA (Commercial Laser),有两家,
26+
一家在 **西湖文化广场** ,还配有12.1声道。还有一家 **万象城店** 就只有5.1声道了。
27+
28+
杜比影院也是具有极佳视听效果的影院。区分于杜比全景声厅,杜比影院会有官方把控质量,
29+
保证放映时应用杜比视界和杜比全景声的效果(理论上),如果没有IMAX COLA影院,
30+
杜比影院也是个不错的选择。据查,杭州的杜比影院共有2家,相比起上文提到的IMAX COLA影院,
31+
离地铁站更远,算是一个小劣势。不过,根据网上的数据, **余杭万达广场店**
32+
的银幕大小和西湖文化广场差不多大,相信观影体验应当是不错的。 **奥体印象城店**
33+
暂无银幕数据。
34+
35+
## 选择影院
36+
37+
IMAX在淘票票等官方途径中搜影城时,不会自己写明是什么版本,只会笼统的写 *IMAX厅*
38+
有混淆视听的嫌疑,而杜比影院和杜比全景声厅则是分开的,比较好看出来。
39+
网上有一张[表格](https://docs.qq.com/sheet/DQ3FEUUZJdklNSWJP?tab=lxy0hx)
40+
统计了全国各地的IMAX和杜比影院影城可以做参考。
41+
42+
一般来说,选择的优先级是`IMAX GT >> IMAX COLA == Dolby Cinema > IMAX Laser XT >> IMAX 氙灯`
43+
44+
除了在线表格,也可以选择小程序来查电影院,分别是"IMAX PLUS会员中心"和"杜比Dolby
45+
One",要注意的是IMAX小程序中没有写明放映技术,只会标明是"激光IMAX"(区别于氙灯),
46+
不会写第几代。
47+
48+
## 参考
49+
50+
1. [应用图片来源](https://new.qq.com/rain/a/20240812A06VZL00)
51+
2. [全球IMAX及其他影厅分布](https://docs.qq.com/sheet/DQ3FEUUZJdklNSWJP?tab=lxy0hx)
+317
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
---
2+
title: 优化,还是过优化?
3+
date: 2024/10/20 19:14:00
4+
updated: 2024/10/20 19:14:00
5+
tags:
6+
- not-ctf
7+
---
8+
<!-- excerpt -->
9+
10+
## 起因
11+
12+
在配置tmux的时候,我希望能看到温度、充电状态和点亮,这三个属性分别存放在三个文件中:
13+
`/sys/class/thermal/thermal_zone0/temp`, `/sys/class/power_supply/ACAD/online`,
14+
`/sys/class/power_supply/BAT1/capacity`中,并配置为这样的显示效果:
15+
16+
<img src="/assets/trueblog/tempAndBat.png" height="30%" width="30%">
17+
18+
为了得到这样的效果,需要在`.tmux.conf`中配置一长串命令:
19+
`#(cut -c -2 /sys/class/thermal/thermal_zone0/temp)C #([ $(cat /sys/class/power_supply/ACAD/online) = 1 ] && echo +)#(cat /sys/class/power_supply/BAT1/capacity)%`
20+
21+
## 开始优化
22+
23+
这实在是太长了!为了简化一些工作,我把显示温度单独抽了出来,用C语言写了一遍
24+
25+
```c showtemp.c
26+
#include <unistd.h>
27+
#include <fcntl.h>
28+
29+
int main(void) {
30+
char buf[3];
31+
buf[2] = 'C';
32+
int fd = open("/sys/class/thermal/thermal_zone0/temp", O_RDONLY);
33+
read(fd, buf, 2);
34+
write(STDOUT_FILENO, buf, 3);
35+
}
36+
```
37+
38+
然后使用`gcc -O3 -s -o showtemp showtemp.c`将其编译为了elf来替换上面打印温度的部分
39+
40+
## 继续优化
41+
42+
一段时间后,我回看这段命令,感觉仍然有点长,然后想到动不动就两三万的pid,
43+
每3秒就要刷新一次,每次要起4个进程,还是太低效了。于是我直接把所有打印集成到一个程序中
44+
45+
```c showtemp.c
46+
#include <string.h>
47+
#include <unistd.h>
48+
#include <fcntl.h>
49+
50+
int main(void) {
51+
char symbol[1], buf[15];
52+
int temp_fd, bat_fd, bat_info_fd;
53+
int readin, padded = 0;
54+
55+
if ((temp_fd = open("/sys/class/thermal/thermal_zone0/temp", O_RDONLY)) < 0) {
56+
memcpy(buf, "-1C ", 4);
57+
padded = 4;
58+
} else {
59+
readin = read(temp_fd, buf, 2);
60+
if (readin == 0) {
61+
memcpy(buf, "??C ", 4);
62+
padded = 4;
63+
close(temp_fd);
64+
goto next_state;
65+
}
66+
if (buf[1] == '\n') // following return
67+
readin--;
68+
memcpy(buf + readin, "C ", 2);
69+
padded = readin + 2;
70+
close(temp_fd);
71+
}
72+
73+
next_state:
74+
if ((bat_fd = open("/sys/class/power_supply/ACAD/online", O_RDONLY)) < 0) {
75+
buf[padded++] = '?';
76+
} else {
77+
readin = read(bat_fd, symbol, 1);
78+
if (readin == 0) {
79+
buf[padded++] = 'x';
80+
close(bat_fd);
81+
} else {
82+
if (*symbol == '1')
83+
buf[padded++] = '+';
84+
close(bat_fd);
85+
}
86+
}
87+
88+
if ((bat_info_fd = open("/sys/class/power_supply/BAT1/capacity", O_RDONLY)) < 0) {
89+
memcpy(buf + padded, "??%", 3);
90+
padded += 3;
91+
} else {
92+
readin = read(bat_info_fd, buf + padded, 3);
93+
if (readin == 0) {
94+
memcpy(buf + padded, "xx%", 3);
95+
padded += 3;
96+
close(bat_info_fd);
97+
goto write_state;
98+
}
99+
if (buf[padded + readin - 1] == '\n')
100+
readin--;
101+
padded += readin;
102+
buf[padded++] = '%';
103+
close(bat_info_fd);
104+
}
105+
106+
write_state:
107+
write(STDOUT_FILENO, buf, padded);
108+
}
109+
```
110+
111+
这次我加入了这种边界判断,然后做了完善的测试,把所有打印功能集成到一个程序中,
112+
然而...
113+
114+
## 陷入疯狂
115+
116+
当我在使用gdb调试程序的时候,我看到了熟悉的动态绑定:这个每天都要被运行几千次的程序,
117+
还需要到ld里加载`open`等函数?疑似有点过于低效了!
118+
119+
我用`gcc -S -o showtemp.s -O3 showtemp.c`将程序转换为汇编,然后手动把call
120+
`open`的地方转写为syscall,再编译:`gcc -nostdlib -O3 -s -static -o showtemp showtemp.s`
121+
122+
继续调试,发现程序不是线性运行的,因为编译器输出汇编时,分支跳转并不是按最高可能的情况输出的。
123+
为此,我又加入了以下代码应用于分支判断加速
124+
125+
```c
126+
#define likely(cond) __builtin_expect((cond), 1)
127+
#define unlikely(cond) __builtin_expect((cond), 0)
128+
```
129+
并且给整数变量前加上了`register`声明,最后再生成为汇编,并手动syscall,
130+
得到了如下的汇编
131+
132+
```asm showtemp.S
133+
.file "showtemp.c"
134+
.intel_syntax noprefix
135+
.text
136+
.align 8
137+
.section .rodata
138+
.equ SYS_read, 0
139+
.equ SYS_write, 1
140+
.equ SYS_open, 2
141+
.equ SYS_close, 3
142+
.equ SYS_exit, 60
143+
.LC0:
144+
.string "/sys/class/thermal/thermal_zone0/temp"
145+
.align 8
146+
.LC1:
147+
.string "/sys/class/power_supply/ACAD/online"
148+
.align 8
149+
.LC2:
150+
.string "/sys/class/power_supply/BAT1/capacity"
151+
.p2align 4
152+
153+
.section .text
154+
.global _start
155+
_start:
156+
.LFB6:
157+
xor esi, esi
158+
xor eax, eax
159+
lea rdi, .LC0[rip]
160+
sub rsp, 40
161+
mov eax, SYS_open
162+
syscall
163+
test eax, eax
164+
js .L16
165+
lea r12, 16[rsp]
166+
mov edx, 2
167+
mov edi, eax
168+
mov ebp, eax
169+
mov rsi, r12
170+
mov eax, SYS_read
171+
syscall
172+
mov r13, rax
173+
mov ebx, eax
174+
test eax, eax
175+
je .L17
176+
.L4:
177+
cmp BYTE PTR 17[rsp], 10
178+
je .L18
179+
.L5:
180+
movsx rax, ebx
181+
mov edx, 8259
182+
mov edi, ebp
183+
add ebx, 2
184+
mov WORD PTR [r12+rax], dx
185+
mov eax, SYS_close
186+
syscall
187+
.L3:
188+
xor esi, esi
189+
lea rdi, .LC1[rip]
190+
xor eax, eax
191+
mov eax, SYS_open
192+
syscall
193+
mov ebp, eax
194+
test eax, eax
195+
js .L19
196+
lea rsi, 15[rsp]
197+
mov edx, 1
198+
mov edi, eax
199+
mov eax, SYS_read
200+
syscall
201+
test eax, eax
202+
je .L20
203+
cmp BYTE PTR 15[rsp], 49
204+
jne .L9
205+
movsx rax, ebx
206+
add ebx, 1
207+
mov BYTE PTR 16[rsp+rax], 43
208+
.L9:
209+
mov edi, ebp
210+
mov eax, SYS_close
211+
syscall
212+
.L7:
213+
xor esi, esi
214+
lea rdi, .LC2[rip]
215+
xor eax, eax
216+
movsx r15, ebx
217+
mov eax, SYS_open
218+
syscall
219+
add r15, r12
220+
mov r14d, eax
221+
test eax, eax
222+
js .L21
223+
mov edx, 3
224+
mov rsi, r15
225+
mov edi, eax
226+
mov eax, SYS_read
227+
syscall
228+
mov r13, rax
229+
mov ebp, eax
230+
test eax, eax
231+
je .L22
232+
.L12:
233+
lea eax, -1[rbx+r13]
234+
cdqe
235+
cmp BYTE PTR 16[rsp+rax], 10
236+
jne .L13
237+
lea ebp, -1[r13]
238+
.L13:
239+
lea eax, 0[rbp+rbx]
240+
mov edi, r14d
241+
lea ebx, 1[rax]
242+
cdqe
243+
mov BYTE PTR 16[rsp+rax], 37
244+
mov eax, SYS_close
245+
syscall
246+
.L11:
247+
movsx rdx, ebx
248+
mov rsi, r12
249+
mov edi, 1
250+
mov eax, SYS_write
251+
syscall
252+
xor edi, edi
253+
mov eax, SYS_exit
254+
syscall
255+
.L19:
256+
movsx rax, ebx
257+
add ebx, 1
258+
mov BYTE PTR 16[rsp+rax], 63
259+
jmp .L7
260+
.L21:
261+
mov WORD PTR [r15], 16191
262+
add ebx, 3
263+
mov BYTE PTR 2[r15], 37
264+
jmp .L11
265+
.L16:
266+
mov DWORD PTR 16[rsp], 541274413
267+
mov ebx, 4
268+
lea r12, 16[rsp]
269+
jmp .L3
270+
.L18:
271+
lea ebx, -1[r13]
272+
jmp .L5
273+
.L20:
274+
lea r13d, 1[rbx]
275+
mov edi, ebp
276+
movsx rbx, ebx
277+
mov BYTE PTR 16[rsp+rbx], 120
278+
mov ebx, r13d
279+
mov eax, SYS_close
280+
syscall
281+
jmp .L7
282+
.L22:
283+
mov WORD PTR [r15], 30840
284+
mov edi, r14d
285+
add ebx, 3
286+
mov BYTE PTR 2[r15], 37
287+
mov eax, SYS_close
288+
syscall
289+
jmp .L11
290+
.L17:
291+
mov edi, ebp
292+
mov DWORD PTR 16[rsp], 541278015
293+
mov ebx, 4
294+
mov eax, SYS_close
295+
syscall
296+
jmp .L3
297+
.LFE6:
298+
.ident "GCC: (GNU) 14.2.1 20240805"
299+
.section .note.GNU-stack,"",@progbits
300+
```
301+
302+
调试的时候也是成功使得程序运行时没有发生任何jmp
303+
304+
## 回顾
305+
306+
当我写完程序后,回过头来看花了过多的时间,因为汇编实在是晦涩难懂,
307+
本来我还想着把逻辑梳理一遍,最后只把call转换成syscall就草草了事。而且,
308+
花这么多时间来“优化”真的值得吗?本来程序运行就花不了多少毫秒,又是把程序转成汇编,
309+
又是加各种优化的,并不能快多少。但是不优化的话,又让我感觉自己没有尽力,
310+
这就是完美主义者的困扰吧。
311+
312+
最近又看到了关于cpu分支判断的文章,原先我以为分支判断错误惩罚是遇到了需要跳转的情况,
313+
可事实上cpu会智能判断是否需要跳转,由此加速程序运行。考虑到优化到这种程序以后,
314+
程序的瓶颈已经到syscall上了,这样的优化看起来就更没必要了。
315+
316+
就像网上说的,优化并不是以自己的范畴优化所有代码,而是重点优化影响系统运行的,
317+
耗时的操作,将你的时间,用在更加重要的事上吧。

‎source/_posts/trueblog/patchelfForZsh.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ updated: 2024/7/30 10:53:00
55
tags:
66
- tricks
77
- not-ctf
8-
thumbnail: /images/patchelfFix.png
8+
thumbnail: /assets/trueblog/patchelfFix.png
99
---
1010

1111
<!--excerpt-->

‎source/_posts/trueblog/ptrace.md

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
---
2+
title: Ptrace syscall 初学者常见困惑解答
3+
date: 2024/10/21 03:27:00
4+
updated: 2024/10/21 03:27:00
5+
tags:
6+
- tricks
7+
- ptrace
8+
---
9+
<!-- excerpt -->
10+
11+
在羊城杯2024的 **Hard Sandbox** 中,有`RET_TRACE`的沙箱,因此如何构造rce并绕过限制的问题,
12+
就来到了如何使用ptrace上。ptrace作为Linux的syscall,已经存在很久了,相关的文档也很完善,
13+
具体细则可以通过`man ptrace`查看,这篇博客主要讲讲一些值得注意的点。
14+
15+
## PTRACE_ATTACH or PTRACE_TRACEME ?
16+
17+
attach是tracer主动发送请求到tracee,通知tracee停下来(SIGSTOP),
18+
使其进入能被调试的状态,而traceme则是tracee通知tracer,使tracer能够调试tracee。
19+
不同于attach,tracee在做traceme并不会停止,因此tracer无法进行调试,
20+
需要tracee做点什么,停下来,例如`raise(SIGTRAP)`
21+
22+
```c
23+
int child = fork();
24+
int status;
25+
#ifdef USE_ATTACH
26+
if (child) {
27+
ptrace(PTRACE_ATTACH, child, 0, 0);
28+
wait(&status);
29+
...
30+
} else {
31+
while (1)
32+
dosomething(); // stops immediately
33+
}
34+
#else
35+
if (child) {
36+
wait(&status);
37+
...
38+
} else {
39+
ptrace(PTRACE_TRACEME, 0, 0, 0);
40+
raise(SIGTRAP); // without this, tracee won't stop!
41+
while (1)
42+
dosomething();
43+
}
44+
#endif
45+
```
46+
47+
## PTRACE_ATTACH or PTRACE_SEIZE ?
48+
49+
相比起attach,seize虽然也是主动使tracee进入被调试的状态,但是tracee不会停止,
50+
也就是说,tracee同样需要自行停止,tracer才能介入。seize一般用于动态监测进程操作,
51+
如strace,不用于调试程序,在ctf中应用相对较少。
52+
53+
## gpt给我的答复在使用traceme时没有raise信号啊?
54+
55+
询问gpt traceme的使用案例后,往往在tracee执行traceme后,立刻执行`exec*`系列函数,
56+
这是因为当发生`execve`系统调用时,在进程空间刚刚开始替换时,tracee会收到SIGTRAP,
57+
然后tracer就可以进行调试,在程序加载完毕前做点什么。
58+
59+
## 如果父进程不希望调试tracee,但是tracee发出了traceme的请求并停止了会如何?
60+
61+
例如有如下代码,运行后终端会如何响应呢
62+
63+
```c
64+
#include <signal.h>
65+
#include <sys/ptrace.h>
66+
#include <stdio.h>
67+
68+
int main(void) {
69+
puts("start");
70+
ptrace(PTRACE_TRACEME, 0, 0, 0);
71+
raise(SIGHUP);
72+
puts("end");
73+
}
74+
```
75+
76+
随便使用一个信号使tracee停止,随后可见tracer能继续操作了,但是若使用`fg`继续运行程序,
77+
则程序无法继续运行,且按`Ctrl-C`无效,只能`kill -9`,
78+
且从`ps -aux`中可以看到tracee的状态是`t`(stopped by debugger during the tracing)
79+
80+
相比起在终端中按`Ctrl-Z`显示"suspended"不同,suspend操作是由shell发送的,
81+
并且ps结果中进程状态是`T`(stopped by job control signal),而shell并不会恢复tracee的状态。
82+
83+
![tracerZsh](/assets/trueblog/tracerZsh.png)
84+
85+
{% note purple fa-circle-arrow-right %}
86+
没有ptraceme的话,由于没有注册SIGHUP信号的处理函数,因此程序会被直接退出
87+
{% endnote %}
88+
89+
## `<defunct>`是什么?
90+
91+
当子进程结束后,父进程没有`wait`,则子进程会陷入僵尸进程的状态,会持续占用资源,
92+
且无法被kill,只有父进程`wait`或退出,子进程的资源才能被释放。
93+
94+
## PTRACE的唯一性
95+
96+
当程序已经成为tracee,且tracer还在调试时,其他进程不得附加到tracee上,
97+
亦即tracee和tracer是一一对应的,这也会导致子进程难以调试。具体来说,
98+
如果使用gdb调试,默认会自动附加到子进程上,此时trace的关系已经建立,
99+
子进程的traceme就会失败(EPERM);或者gdb仍然附加在父进程上,然后tracee执行traceme后,
100+
gdb再attach到子进程上,那gdb就会attach失败(EPERM)。
101+
102+
## gdb无法使用attach调试到特定的pid?
103+
104+
这是由于权限设置问题,默认情况下,如果tracer没有CAP_SYS_PTRACE且和tracee没有任何pid上的继承关系,
105+
则无法attach,这时候需要通过`echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope`的方式解除限制,
106+
当然,这么做并不安全!可能会导致恶意程序attach到系统关键进程上并读取数据等。
107+
108+
## 题目的总体思路
109+
110+
以下大致是参考文章中题解汇编的C代码实现
111+
112+
```c
113+
#include <signal.h>
114+
#include <sys/ptrace.h>
115+
#include <sys/wait.h>
116+
#include <unistd.h>
117+
118+
int main(void) {
119+
int child = fork();
120+
if (child) {
121+
// parent
122+
int status;
123+
wait(&status);
124+
ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESECCOMP);
125+
do {
126+
ptrace(PTRACE_CONT, child, 0, 0);
127+
wait(&status);
128+
} while (!WIFEXITED(status));
129+
return 0;
130+
} else {
131+
// child
132+
ptrace(PTRACE_TRACEME, 0, 0, 0);
133+
raise(SIGSTOP);
134+
execve("/bin/sh", NULL, NULL);
135+
return 0;
136+
}
137+
}
138+
```
139+
140+
{% note yellow fa-exclamation %}
141+
`raise(SIGSTOP)`不可省略!虽然上文提到`execve`会使tracee收到SIGTRAP,但是在这之前,
142+
由于沙箱的限制,子进程会在父进程能够设置ptrace选项前退出,因此父进程不再能调试子进程。
143+
因此通过显式发出信号,可以让父进程有接管子进程的时间
144+
145+
一开始我以为`fork`会使子进程从`main`函数重新开始,实际上不是的,`fork`后,
146+
父子进程的rip都在`fork`后的下一句话
147+
{% endnote %}
148+
149+
## 参考
150+
151+
1. [羊城杯 2024 pwn writeup#sandbox(after competition)](https://qanux.github.io/2024/08/28/%E7%BE%8A%E5%9F%8E%E6%9D%AF%202024%20pwn%20writeup/index.html#sandbox-after-competition)
152+
2. [Linux沙箱之ptrace](https://blog-archive.betamao.me/2019/02/02/Linux%E6%B2%99%E7%AE%B1%E4%B9%8Bptrace/)
153+
3. [linux下僵尸进程(Defunct进程)的产生与避免](https://zhuanlan.zhihu.com/p/356414911)
92.2 KB
Loading
+11
Loading
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

‎source/assets/trueblog/tempAndBat.png

5.33 KB
Loading

‎source/assets/trueblog/tracerZsh.png

111 KB
Loading
File renamed without changes.

‎source/assets/trueblog/wandering.jpg

441 KB
Loading

0 commit comments

Comments
 (0)
Please sign in to comment.