-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathatom.xml
executable file
·505 lines (273 loc) · 531 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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>o0xmuhe's blog</title>
<subtitle>0x4142434445464748 ????????</subtitle>
<link href="https://o0xmuhe.github.io/atom.xml" rel="self"/>
<link href="https://o0xmuhe.github.io/"/>
<updated>2023-09-09T08:26:59.674Z</updated>
<id>https://o0xmuhe.github.io/</id>
<author>
<name>muhe</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>Chromium based browser/Webview启用--js-flags</title>
<link href="https://o0xmuhe.github.io/2023/09/08/Chromium-based-browser-Webview%E5%90%AF%E7%94%A8-js-flags/"/>
<id>https://o0xmuhe.github.io/2023/09/08/Chromium-based-browser-Webview%E5%90%AF%E7%94%A8-js-flags/</id>
<published>2023-09-08T09:44:55.000Z</published>
<updated>2023-09-09T08:26:59.674Z</updated>
<content type="html"><![CDATA[<h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>因为工作需求要去验证一些PoC,而很多PoC需要开natives-syntax才能跑,如果转成纯JS实现又需要花更多时间,所以需要在Android的app/webview里也实现添加 js-flags,方便后面搞分析 :)</p><span id="more"></span><h2 id="PC"><a href="#PC" class="headerlink" title="PC"></a>PC</h2><p>PC上比较简单没啥好说的,直接命令行传递参数就行</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./chrome --js-flags=<span class="string">"--allow-natives-syntax"</span></span><br></pre></td></tr></table></figure><h2 id="Android"><a href="#Android" class="headerlink" title="Android"></a>Android</h2><p>相关的代码,可以知道参数配置的文件相关情况</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/20230908174150.png" alt="image.png"></p><p>具体的操作步骤:</p><ol><li><p>chrome://flags里开启<code>Enable command line on non-rooted devices</code></p></li><li><p>把启动参数写到 /data/local/tmp 下,文件名固定</p></li></ol><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> <span class="string">"chrome --js-flags=\"--expose-gc --allow-natives-syntax\""</span> > /data/<span class="built_in">local</span>/tmp/chrome-command-line</span><br></pre></td></tr></table></figure><p>然后就可以验证PoC了<br><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/20230908174211.png" alt="image.png"></p><h2 id="Webview"><a href="#Webview" class="headerlink" title="Webview"></a>Webview</h2><h3 id="系统webview"><a href="#系统webview" class="headerlink" title="系统webview"></a>系统webview</h3><p><a href="https://developer.android.com/develop/ui/views/layout/webapps/debugging#java">https://developer.android.com/develop/ui/views/layout/webapps/debugging#java</a></p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/20230908174224.png" alt="image.png"></p><p>主要是依赖这个 DevTools来做的,参考:<a href="https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/developer-ui.md">https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/developer-ui.md</a></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">am start -a <span class="string">"com.android.webview.SHOW_DEV_UI"</span></span><br></pre></td></tr></table></figure><p>这样看到的flags配置,并没有开启命令行选项的配置,这个和浏览器不太一样,参考:</p><p><a href="https://chromium.googlesource.cm/chromium/src/+/HEAD/android_webview/docs/commandline-flags.md">https://chromium.googlesource.cm/chromium/src/+/HEAD/android_webview/docs/commandline-flags.md</a></p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/20230908174236.png" alt="image.png"></p><p>文件路径是 <code>/data/local/tmp/webview-command-line</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></pre></td><td class="code"><pre><span class="line">FLAG_FILE=/data/local/tmp/webview-command-line</span><br><span class="line"># Overwrite <span class="built_in">flags</span> (supports multiple). The first token is ignored. We use <span class="string">'_'</span></span><br><span class="line"><span class="meta"># as a convenient placeholder, but any token is acceptable.</span></span><br><span class="line">adb shell <span class="string">"echo '_ --highlight-all-webviews --force-enable-metrics-reporting' > ${FLAG_FILE}"</span></span><br><span class="line"># Clear flags</span><br><span class="line">adb shell <span class="string">"rm ${FLAG_FILE}"</span></span><br><span class="line"># Print flags</span><br><span class="line">adb shell <span class="string">"cat ${FLAG_FILE}"</span></span><br></pre></td></tr></table></figure><p>这样不太行,所以考虑直接frida hook,要注意因为加载目标类在webview的dex中,所以需要遍历classloader找到对应的classloader去钩目标类</p><figure class="highlight javascript"><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">Java.perform(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> Java.enumerateClassLoaders({</span><br><span class="line"> <span class="attr">onMatch</span>: <span class="function"><span class="keyword">function</span> (<span class="params">loader</span>) </span>{</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">if</span>(loader.findClass(<span class="string">"org.chromium.base.CommandLine"</span>)){</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"[+]Successfully found loader"</span>)</span><br><span class="line"> <span class="built_in">console</span>.log(loader);</span><br><span class="line"> Java.classFactory.loader = loader;</span><br><span class="line"> <span class="keyword">let</span> CommandLine = Java.use(<span class="string">"org.chromium.base.CommandLine"</span>);</span><br><span class="line"> CommandLine.nativeInit.implementation = <span class="function"><span class="keyword">function</span>(<span class="params">...args</span>)</span>{</span><br><span class="line"> <span class="built_in">this</span>.nativeInit(...args);</span><br><span class="line"> <span class="built_in">this</span>.nativeAppendSwitchWithValue(<span class="string">"--js-flags"</span>, <span class="string">"--allow-natives-syntax"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span>(error){</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"[!]Find error:"</span> + error)</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">onComplete</span>: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"[+]onComplete"</span>);</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>完整脚本: <a href="https://gist.github.com/o0xmuhe/98299328206820d2c55a7f44d300cdc0">https://gist.github.com/o0xmuhe/98299328206820d2c55a7f44d300cdc0</a></p><h3 id="APP自己实现的webview"><a href="#APP自己实现的webview" class="headerlink" title="APP自己实现的webview"></a>APP自己实现的webview</h3><p> 和系统的webview做法一样, 找到合适的位置直接hook <code>CommandLine</code> 就行, 不同厂商做法不太一样,这个需要自己逆向一下</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://juejin.cn/post/6847902219757420552">https://juejin.cn/post/6847902219757420552</a><br><a href="https://developer.android.com/develop/ui/views/layout/webapps/debugging#java">https://developer.android.com/develop/ui/views/layout/webapps/debugging#java</a></p>]]></content>
<summary type="html"><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>因为工作需求要去验证一些PoC,而很多PoC需要开natives-syntax才能跑,如果转成纯JS实现又需要花更多时间,所以需要在Android的app/webview里也实现添加 js-flags,方便后面搞分析 :)</p></summary>
<category term="环境配置踩坑" scheme="https://o0xmuhe.github.io/categories/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE%E8%B8%A9%E5%9D%91/"/>
<category term="exploit" scheme="https://o0xmuhe.github.io/tags/exploit/"/>
<category term="chrome" scheme="https://o0xmuhe.github.io/tags/chrome/"/>
<category term="webview" scheme="https://o0xmuhe.github.io/tags/webview/"/>
</entry>
<entry>
<title>批量运营CodeQL Cli扫描结果(简易版)</title>
<link href="https://o0xmuhe.github.io/2023/05/11/%E6%89%B9%E9%87%8F%E8%BF%90%E8%90%A5CodeQL-Cli%E6%89%AB%E6%8F%8F%E7%BB%93%E6%9E%9C-%E7%AE%80%E6%98%93%E7%89%88/"/>
<id>https://o0xmuhe.github.io/2023/05/11/%E6%89%B9%E9%87%8F%E8%BF%90%E8%90%A5CodeQL-Cli%E6%89%AB%E6%8F%8F%E7%BB%93%E6%9E%9C-%E7%AE%80%E6%98%93%E7%89%88/</id>
<published>2023-05-11T13:14:08.000Z</published>
<updated>2023-05-16T12:03:58.981Z</updated>
<content type="html"><![CDATA[<h2 id="背景-amp-目标"><a href="#背景-amp-目标" class="headerlink" title="背景&目标"></a>背景&目标</h2><p> <code>CodeQL Cli</code>适合批量做扫描,但是扫描结果并不适合直接做批量的运营,仅适合一些实锤的问题,对于一些还需要人工处理判断的结果就不太适合了(要看源码、调用上下文);如果使用<code>VSCode</code>插件来做,也只是单条规则扫单个/多个数据库,结果倒是很友好,点点点就能读代码来分析了,所以这种用法不适合批量的<code>query</code>扫描。</p><p> 如果付费的话自然是可以解决了,可以在<code>CI/CD</code>中集成,就方便多了 -。- 但是对于个人使用者来说不太现实,所以我就想用一个简单的办法来实现这个目的</p><p><code>CodeQL cli</code>批量扫描结束后,导入数据库+历史<code>query</code>结果,直接在<code>vscode</code>里运营结果,流程为:</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">CI/CD 扫描 ---> 结果(数据库+扫描结果) ---> 导入VSCode运营</span><br></pre></td></tr></table></figure><span id="more"></span><p>目标拆解</p><ol><li> 批量导入数据库,而不是通过<code>GUI</code>点点点导入</li><li> 导入扫描结果,历史<code>query</code>不要清理,为的是把<code>cli</code>的扫描结果导入对应目录之后可以直接在<code>CodeQL</code>的<code>query history</code>中看到</li></ol><h2 id="工作流程"><a href="#工作流程" class="headerlink" title="工作流程"></a>工作流程</h2><h3 id="批量导入数据库"><a href="#批量导入数据库" class="headerlink" title="批量导入数据库"></a>批量导入数据库</h3><p>查看日志,猜测是类似的做法,解析某个配置文件,然后导入,所以要么修改文件,要么直接把数据库目录<code>copy</code>过去就行</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></pre></td><td class="code"><pre><span class="line">Initializing database manager.</span><br><span class="line">Found 1 persisted databases: file:///home/muhe/Work/codeql_multi_work/XNU-revision-2018-October-28--14-31-48</span><br><span class="line">Initializing database panel.</span><br><span class="line">Initializing evaluator <span class="built_in">log</span> viewer.</span><br><span class="line">Initializing query <span class="built_in">history</span> manager.</span><br><span class="line">Initializing results panel interface</span><br></pre></td></tr></table></figure><p>存储位置</p><figure class="highlight typescript"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * databases.ts</span></span><br><span class="line"><span class="comment"> * ------------</span></span><br><span class="line"><span class="comment"> * Managing state of what the current database is, and what other</span></span><br><span class="line"><span class="comment"> * databases have been recently selected.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * The source of truth of the current state resides inside the</span></span><br><span class="line"><span class="comment"> * `DatabaseManager` class below.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The name of the key in the workspaceState dictionary in which we</span></span><br><span class="line"><span class="comment"> * persist the current database across sessions.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> CURRENT_DB = <span class="string">"currentDatabase"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The name of the key in the workspaceState dictionary in which we</span></span><br><span class="line"><span class="comment"> * persist the list of databases across sessions.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> DB_LIST = <span class="string">"databaseList"</span>;</span><br></pre></td></tr></table></figure><p>导出信息就能看到了,所以直接改数据库就能添加多个数据库了</p><p><code>/home/muhe/.config/Code/User/workspaceStorage/693bdf324f8bd69cec87e06d65e8d000/state.vscdb</code> </p><figure class="highlight json"><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><br><span class="line"> <span class="attr">"databaseList"</span>: [</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"uri"</span>: <span class="string">"file:///home/muhe/Work/codeql_multi_work/XNU-revision-2018-October-28--14-31-48"</span>,</span><br><span class="line"> <span class="attr">"options"</span>: {</span><br><span class="line"> <span class="attr">"ignoreSourceArchive"</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">"dateAdded"</span>: <span class="number">1684233047172</span>,</span><br><span class="line"> <span class="attr">"language"</span>: <span class="string">"cpp"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ],</span><br><span class="line"> <span class="attr">"currentDatabase"</span>: <span class="string">"file:///home/muhe/Work/codeql_multi_work/XNU-revision-2018-October-28--14-31-48"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>尝试直接修改这个数据库 就可以批量导入了,不需要挨个点点点了 :) </p><h3 id="导入扫描结果"><a href="#导入扫描结果" class="headerlink" title="导入扫描结果"></a>导入扫描结果</h3><h4 id="query-history"><a href="#query-history" class="headerlink" title="query-history"></a><code>query-history</code></h4><p>扫描结果的导入是有个json文件描述的</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></pre></td><td class="code"><pre><span class="line">Reading query <span class="built_in">history</span></span><br><span class="line">Reading cached query <span class="built_in">history</span> from <span class="string">'/home/muhe/.config/Code/User/workspaceStorage/693bdf324f8bd69cec87e06d65e8d000/GitHub.vscode-codeql/workspace-query-history.json'</span>.</span><br><span class="line">Successfully finished extension initialization.</span><br><span class="line">CodeQL extension version: 1.8.4 </span><br><span class="line">CodeQL CLI version: 2.13.1 </span><br><span class="line">Platform: linux x64</span><br></pre></td></tr></table></figure><p>我这里随意跑了两个Query,查看这个文件可以看到这两次记录:</p><figure class="highlight json"><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><br><span class="line"> <span class="attr">"version"</span>: <span class="number">2</span>,</span><br><span class="line"> <span class="attr">"queries"</span>: [</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"initialInfo"</span>: {</span><br><span class="line"> <span class="attr">"queryText"</span>: <span class="string">"/**\n * @name Empty block\n * @kind problem\n * @problem.severity warning\n * @id cpp/example/empty-block\n */\n\nimport cpp\n \nfrom BlockStmt b\nwhere b.getNumStmt() = 0\nselect b, \"This is an empty block.\"\n"</span>,</span><br><span class="line"> <span class="attr">"isQuickQuery"</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">"isQuickEval"</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">"queryPath"</span>: <span class="string">"/home/muhe/Tools/vscode-codeql-starter/codeql-custom-queries-cpp/example.ql"</span>,</span><br><span class="line"> <span class="attr">"databaseInfo"</span>: {</span><br><span class="line"> <span class="attr">"databaseUri"</span>: <span class="string">"file:///home/muhe/Work/codeql_multi_work/XNU-revision-2018-October-28--14-31-48"</span>,</span><br><span class="line"> <span class="attr">"name"</span>: <span class="string">"XNU-revision-2018-October-28--14-31-48"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"start"</span>: <span class="string">"2023-05-16T10:44:25.321Z"</span>,</span><br><span class="line"> <span class="attr">"id"</span>: <span class="string">"example.ql-g8Dji9oz8xqxh-XoF96jF"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"t"</span>: <span class="string">"local"</span>,</span><br><span class="line"> <span class="attr">"evalLogLocation"</span>: <span class="string">"/home/muhe/.config/Code/User/globalStorage/github.vscode-codeql/queries/example.ql-DQ3x1MPMGzVEtt7SAYjJ9/evaluator-log.jsonl"</span>,</span><br><span class="line"> <span class="attr">"evalLogSummaryLocation"</span>: <span class="string">"/home/muhe/.config/Code/User/globalStorage/github.vscode-codeql/queries/example.ql-DQ3x1MPMGzVEtt7SAYjJ9/evaluator-log.summary"</span>,</span><br><span class="line"> <span class="attr">"completedQuery"</span>: {</span><br><span class="line"> <span class="attr">"query"</span>: {</span><br><span class="line"> <span class="attr">"querySaveDir"</span>: <span class="string">"/home/muhe/.config/Code/User/globalStorage/github.vscode-codeql/queries/example.ql-DQ3x1MPMGzVEtt7SAYjJ9"</span>,</span><br><span class="line"> <span class="attr">"dbItemPath"</span>: <span class="string">"/home/muhe/Work/codeql_multi_work/XNU-revision-2018-October-28--14-31-48"</span>,</span><br><span class="line"> <span class="attr">"databaseHasMetadataFile"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">"metadata"</span>: {</span><br><span class="line"> <span class="attr">"name"</span>: <span class="string">"Empty block"</span>,</span><br><span class="line"> <span class="attr">"kind"</span>: <span class="string">"problem"</span>,</span><br><span class="line"> <span class="attr">"problem.severity"</span>: <span class="string">"warning"</span>,</span><br><span class="line"> <span class="attr">"id"</span>: <span class="string">"cpp/example/empty-block"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"resultsPaths"</span>: {</span><br><span class="line"> <span class="attr">"resultsPath"</span>: <span class="string">"/home/muhe/.config/Code/User/globalStorage/github.vscode-codeql/queries/example.ql-DQ3x1MPMGzVEtt7SAYjJ9/results.bqrs"</span>,</span><br><span class="line"> <span class="attr">"interpretedResultsPath"</span>: <span class="string">"/home/muhe/.config/Code/User/globalStorage/github.vscode-codeql/queries/example.ql-DQ3x1MPMGzVEtt7SAYjJ9/interpretedResults.sarif"</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"result"</span>: {</span><br><span class="line"> <span class="attr">"runId"</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="attr">"queryId"</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="attr">"resultType"</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="attr">"evaluationTime"</span>: <span class="number">11424</span>,</span><br><span class="line"> <span class="attr">"message"</span>: <span class="string">"finished in 11 seconds"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"successful"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">"message"</span>: <span class="string">"finished in 11 seconds"</span>,</span><br><span class="line"> <span class="attr">"resultCount"</span>: <span class="number">1461</span>,</span><br><span class="line"> <span class="attr">"sortedResultsInfo"</span>: {}</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"initialInfo"</span>: {</span><br><span class="line"> <span class="attr">"queryText"</span>: <span class="string">"/**\n * @name Empty block\n * @kind problem\n * @problem.severity warning\n * @id cpp/example/empty-block\n */\n\nimport cpp\n \nfrom BlockStmt b\nwhere b.getNumStmt() = 0\nselect b, \"This is an empty block.\"\n"</span>,</span><br><span class="line"> <span class="attr">"isQuickQuery"</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">"isQuickEval"</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">"queryPath"</span>: <span class="string">"/home/muhe/Tools/vscode-codeql-starter/codeql-custom-queries-cpp/example.ql"</span>,</span><br><span class="line"> <span class="attr">"databaseInfo"</span>: {</span><br><span class="line"> <span class="attr">"databaseUri"</span>: <span class="string">"file:///home/muhe/Work/codeql_multi_work/XNU-revision-2018-October-28--14-31-48"</span>,</span><br><span class="line"> <span class="attr">"name"</span>: <span class="string">"XNU-revision-2018-October-28--14-31-48"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"start"</span>: <span class="string">"2023-05-16T10:45:23.082Z"</span>,</span><br><span class="line"> <span class="attr">"id"</span>: <span class="string">"example.ql-2I3wLwL9OlUi_h2VbLFuj"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"t"</span>: <span class="string">"local"</span>,</span><br><span class="line"> <span class="attr">"evalLogLocation"</span>: <span class="string">"/home/muhe/.config/Code/User/globalStorage/github.vscode-codeql/queries/example.ql-tPeM-xPnZG3MkZC0duLSE/evaluator-log.jsonl"</span>,</span><br><span class="line"> <span class="attr">"evalLogSummaryLocation"</span>: <span class="string">"/home/muhe/.config/Code/User/globalStorage/github.vscode-codeql/queries/example.ql-tPeM-xPnZG3MkZC0duLSE/evaluator-log.summary"</span>,</span><br><span class="line"> <span class="attr">"completedQuery"</span>: {</span><br><span class="line"> <span class="attr">"query"</span>: {</span><br><span class="line"> <span class="attr">"querySaveDir"</span>: <span class="string">"/home/muhe/.config/Code/User/globalStorage/github.vscode-codeql/queries/example.ql-tPeM-xPnZG3MkZC0duLSE"</span>,</span><br><span class="line"> <span class="attr">"dbItemPath"</span>: <span class="string">"/home/muhe/Work/codeql_multi_work/XNU-revision-2018-October-28--14-31-48"</span>,</span><br><span class="line"> <span class="attr">"databaseHasMetadataFile"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">"metadata"</span>: {</span><br><span class="line"> <span class="attr">"name"</span>: <span class="string">"Empty block"</span>,</span><br><span class="line"> <span class="attr">"kind"</span>: <span class="string">"problem"</span>,</span><br><span class="line"> <span class="attr">"problem.severity"</span>: <span class="string">"warning"</span>,</span><br><span class="line"> <span class="attr">"id"</span>: <span class="string">"cpp/example/empty-block"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"resultsPaths"</span>: {</span><br><span class="line"> <span class="attr">"resultsPath"</span>: <span class="string">"/home/muhe/.config/Code/User/globalStorage/github.vscode-codeql/queries/example.ql-tPeM-xPnZG3MkZC0duLSE/results.bqrs"</span>,</span><br><span class="line"> <span class="attr">"interpretedResultsPath"</span>: <span class="string">"/home/muhe/.config/Code/User/globalStorage/github.vscode-codeql/queries/example.ql-tPeM-xPnZG3MkZC0duLSE/interpretedResults.sarif"</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"result"</span>: {</span><br><span class="line"> <span class="attr">"runId"</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="attr">"queryId"</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="attr">"resultType"</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="attr">"evaluationTime"</span>: <span class="number">31</span>,</span><br><span class="line"> <span class="attr">"message"</span>: <span class="string">"finished in 0 seconds"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"successful"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">"message"</span>: <span class="string">"finished in 0 seconds"</span>,</span><br><span class="line"> <span class="attr">"resultCount"</span>: <span class="number">1461</span>,</span><br><span class="line"> <span class="attr">"sortedResultsInfo"</span>: {}</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>所以把query结果按照这个格式填进去就行了。</p><h4 id="配置文件中需要的CodeQL-cli信息获取"><a href="#配置文件中需要的CodeQL-cli信息获取" class="headerlink" title="配置文件中需要的CodeQL cli信息获取"></a>配置文件中需要的CodeQL cli信息获取</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><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">❯ tree -L 1 .</span><br><span class="line">.</span><br><span class="line">├── evaluator-log-end.summary</span><br><span class="line">├── evaluator-log.jsonl</span><br><span class="line">├── evaluator-log.summary</span><br><span class="line">├── evaluator-log.summary.map</span><br><span class="line">├── interpretedResults.sarif</span><br><span class="line">├── query.log</span><br><span class="line">├── results.bqrs</span><br><span class="line">├── results.dil</span><br><span class="line">└── timestamp</span><br></pre></td></tr></table></figure><p>经过测试,<strong>运营只需要扫描结果就行</strong>,其他的可以忽略</p><ul><li> Evaluator Log 相关可以不要</li><li> DIL 也可以不要,可以用于query调优啥的,我们只运营结果就不考虑了</li></ul><p>FYI: 其他的文件(log、dil等)是为了下面菜单中展示的功能做的:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20230516184801557.png" alt="image-20230516184801557"></p><h2 id="批量query-amp-导入结果分析"><a href="#批量query-amp-导入结果分析" class="headerlink" title="批量query & 导入结果分析"></a>批量query & 导入结果分析</h2><blockquote><p> 一般来说,我们会使用到开源的规则以及自己写的规则,如果有一定的积累的话,自己的规则可以搞成一个qlpack,方便后面对新目标的快速分析或者批量查找问题。</p></blockquote><h3 id="通用规则-开源规则"><a href="#通用规则-开源规则" class="headerlink" title="通用规则/开源规则"></a>通用规则/开源规则</h3><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><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"># muhe @ muhe-NUC11PAHi5 in ~/Tools/vscode-codeql-starter/ql/cpp/ql/src/codeql-suites on git:codeql-cli/latest o [18:53:36]</span></span><br><span class="line">$ tree -L 1 .</span><br><span class="line">.</span><br><span class="line">|-- cpp-code-scanning.qls</span><br><span class="line">|-- cpp-lgtm-full.qls</span><br><span class="line">|-- cpp-lgtm.qls</span><br><span class="line">|-- cpp-security-and-quality.qls</span><br><span class="line">|-- cpp-security-experimental.qls</span><br><span class="line">|-- cpp-security-extended.qls</span><br><span class="line">`-- exclude-slow-queries.yml</span><br><span class="line"></span><br><span class="line">0 directories, 7 files</span><br></pre></td></tr></table></figure><p>比如我们尝试使用<code>cpp-security-and-quality.qls</code>这个规则集跑老版本的XNU作为演示</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">codeql database run-queries --ram=<span class="number">16384</span> --threads=<span class="number">12</span> XNU-revision<span class="number">-2018</span>-October<span class="number">-28</span>-<span class="number">-14</span><span class="number">-31</span><span class="number">-48</span> --min-disk-free=<span class="number">1024</span> -v ~/Tools/vscode-codeql-starter/ql/cpp/ql/src/codeql-suites/cpp-security-and-quality.qls</span><br></pre></td></tr></table></figure><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20230516191008453.png" alt="image-20230516191008453"></p><p>FYI: 可以使用 <code>codeql resolve queries ~/Tools/vscode-codeql-starter/ql/cpp/ql/src/codeql-suites/cpp-security-and-quality.qls --format=text</code> 获取这个规则集包含了哪些query</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20230516185953628.png" alt="image-20230516185953628"></p><h3 id="特有规则"><a href="#特有规则" class="headerlink" title="特有规则"></a>特有规则</h3><p>第二种就使用规则仓库中PICO的pack就行,或者直接指定一个qls扫,就是类似的做法了,比如可以自己搞一个qlpack:</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">codeql database run-queries --ram=16384 --threads=8 --min-disk-free=1024 -- [database] [qlpack]</span><br></pre></td></tr></table></figure><h3 id="结果处理"><a href="#结果处理" class="headerlink" title="结果处理"></a>结果处理</h3><p>对于这种跑query的方式,如果不指定输出,默认结果会放在数据库的 <code>results</code>目录下,比如:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20230516191056186.png" alt="image-20230516191056186"></p><p>所以可以写个脚本</p><ul><li> 修改<code>state.vscdb</code>,批量把<code>codeql db</code>导入</li><li> 修改<code>query-history</code>文件,把扫描结果导入</li></ul><h2 id="最终效果"><a href="#最终效果" class="headerlink" title="最终效果"></a>最终效果</h2><p>最终实现的效果如下 :) </p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20230516195840932.png" alt="image-20230516195840932"></p><p>FYI: 两个关键文件的路径不同平台下大同小异:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> <span class="string">'macOS'</span> <span class="keyword">in</span> current_platform:</span><br><span class="line"> globalStorage = <span class="string">f'<span class="subst">{os.getenv(<span class="string">"HOME"</span>)}</span>/Library/Application Support/Code/User/globalStorage'</span></span><br><span class="line"> workspaceStorage = <span class="string">f'<span class="subst">{os.getenv(<span class="string">"HOME"</span>)}</span>/Library/Application Support/Code/User/workspaceStorage'</span></span><br><span class="line"><span class="keyword">elif</span> <span class="string">'Linux'</span> <span class="keyword">in</span> current_platform:</span><br><span class="line"> globalStorage = <span class="string">f'<span class="subst">{os.getenv(<span class="string">"HOME"</span>)}</span>/.config/Code/User/globalStorage'</span></span><br><span class="line"> workspaceStorage = <span class="string">f'<span class="subst">{os.getenv(<span class="string">"HOME"</span>)}</span>/.config/Code/User/workspaceStorage'</span></span><br><span class="line"><span class="keyword">elif</span> <span class="string">'Windows'</span> <span class="keyword">in</span> current_platform:</span><br><span class="line"> globalStorage = <span class="string">f'<span class="subst">{os.getenv(<span class="string">"APPDATA"</span>)}</span>\\Code\\User\\globalStorage'</span></span><br><span class="line"> workspaceStorage = <span class="string">f'<span class="subst">{os.getenv(<span class="string">"APPDATA"</span>)}</span>\\Code\\User\\workspaceStorage'</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"> <span class="comment"># error</span></span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><p> <a href="https://docs.github.com/en/code-security/codeql-cli/using-the-codeql-cli/analyzing-databases-with-the-codeql-cli#running-codeql-database-analyze">Analyzing databases with the CodeQL CLI</a></p></li><li><p> <a href="https://docs.github.com/en/code-security/codeql-cli/codeql-cli-reference/about-codeql-packs">About CodeQL packs</a></p></li><li><p> <a href="https://sumsec.me/2022/CodeQL-Usage-Tricks.html">CodeQl Usage Tricks</a></p></li></ul>]]></content>
<summary type="html"><h2 id="背景-amp-目标"><a href="#背景-amp-目标" class="headerlink" title="背景&amp;目标"></a>背景&amp;目标</h2><p> <code>CodeQL Cli</code>适合批量做扫描,但是扫描结果并不适合直接做批量的运营,仅适合一些实锤的问题,对于一些还需要人工处理判断的结果就不太适合了(要看源码、调用上下文);如果使用<code>VSCode</code>插件来做,也只是单条规则扫单个/多个数据库,结果倒是很友好,点点点就能读代码来分析了,所以这种用法不适合批量的<code>query</code>扫描。</p>
<p> 如果付费的话自然是可以解决了,可以在<code>CI/CD</code>中集成,就方便多了 -。- 但是对于个人使用者来说不太现实,所以我就想用一个简单的办法来实现这个目的</p>
<p><code>CodeQL cli</code>批量扫描结束后,导入数据库+历史<code>query</code>结果,直接在<code>vscode</code>里运营结果,流程为:</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">CI/CD 扫描 ---&gt; 结果(数据库+扫描结果) ---&gt; 导入VSCode运营</span><br></pre></td></tr></table></figure></summary>
<category term="工具" scheme="https://o0xmuhe.github.io/categories/%E5%B7%A5%E5%85%B7/"/>
<category term="CodeQL" scheme="https://o0xmuhe.github.io/tags/CodeQL/"/>
</entry>
<entry>
<title>最近看过的议题&文章(Bootloader/TZ)</title>
<link href="https://o0xmuhe.github.io/2022/12/31/%E6%9C%80%E8%BF%91%E7%9C%8B%E8%BF%87%E7%9A%84%E8%AE%AE%E9%A2%98-%E6%96%87%E7%AB%A0-Bootloader-TZ/"/>
<id>https://o0xmuhe.github.io/2022/12/31/%E6%9C%80%E8%BF%91%E7%9C%8B%E8%BF%87%E7%9A%84%E8%AE%AE%E9%A2%98-%E6%96%87%E7%AB%A0-Bootloader-TZ/</id>
<published>2022-12-31T08:27:16.000Z</published>
<updated>2023-02-14T14:44:26.485Z</updated>
<content type="html"><![CDATA[<h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p> 最近看了一些<code>Bootloader&TZ</code>以及相关的议题,主要是ARM架构下的内容;正好这几个月我的<code>Leader</code>领着我们组一起学习<code>ARMv8&v9</code>架构相关的知识,在阅读这些材料的时候给我提供了不少的帮助,让我理解起来更加容易,也算是变相检验学习成果咯。</p><span id="more"></span><p>于是我便有了这样的感慨 :)</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221231181650825.png" alt="image-20221231181650825"></p><h2 id="Security-Boot-amp-Bootloader相关"><a href="#Security-Boot-amp-Bootloader相关" class="headerlink" title="Security Boot & Bootloader相关"></a>Security Boot & Bootloader相关</h2><p>没看完的材料就是TBD的状态 :(</p><ul><li><p><a href="https://github.com/hhj4ck/BLUnlock/blob/master/ISC2017.pdf">没钥匙也要拧开BOOTLOADER的锁</a> - Guanxing Wen, ISC, 2017</p><blockquote><p> 厂商在ABL里增加unlock bl验证逻辑,针对这部分的安全性研究</p></blockquote></li><li><p><a href="https://github.com/hhj4ck/BLUnlock/blob/master/ISC2018.pdf">启动链脆弱性分析</a> - Guanxing Wen, ISC, 2018</p><blockquote><p> 三星的安全启动分析,攻击TZ实现绕过锁屏码; reference里 <code>@NWMonster 三星的分析和利⽤</code> 我也没找到:( 可惜</p></blockquote></li><li><p><a href="https://speakerdeck.com/hhj4ck/el3-tour-get-the-ultimate-privilege-of-android-phone">EL3 Tour: Get The Ultimate Privilege of Android Phone</a> - Guanxing Wen, Infiltrate, 2019</p><blockquote><p> 华为的安全启动探究,利用bootrom漏洞实现打破信任链,从而实现拿到EL1、EL3的权限,然后攻击TEE,非常精彩的议题;需要ARMv8架构相关的知识,理解起来会更轻松 :)</p></blockquote></li><li><p><a href="https://raw.githubusercontent.com/hhj4ck/checkm30/master/checkm30.pdf">Checkmate Mate30</a> - Slipper & Guanxing Wen, <em>MOSEC</em>, 2021</p><blockquote><p> 华为Mate30的BootROM漏洞挖掘&利用,和之前EL3 Tour那个类似;但是华为通过OTA修了这个洞也是很神奇,<del>不知道是不是用的ARM FPB特性做的</del></p></blockquote></li><li><p><a href="">MediAttack - break the boot chain of MediaTek SoC</a> - neoni, MOSEC, 2022</p><blockquote><p> MTK安全启动分析以及BootROM漏洞挖掘&利用,打破信任链后可以实现对任意分区读写、解密数据等,配合<a href="https://github.com/MTK-bypass/bypass_utility_">mtk-bypass</a>阅读体验更好</p></blockquote></li><li><p><a href="https://raw.githubusercontent.com/TaszkSecLabs/presentations/main/US-21-Komaromy-How-To-Tame-Your-Unicorn.pdf">How To Tame Your Unicorn</a> - Daniel Komaromy & Lorant Szabo, <em>Black Hat USA</em>, 2021</p><blockquote><p> 打华为的基带,顺带BootROM的漏洞,配合白皮书阅读体验更佳</p></blockquote></li><li><p><a href="https://labs.taszk.io/articles/post/huawei_kirin990_bootrom_patch/">Test Point Break: Analysis of Huawei’s OTA Fix For BootROM Vulnerabilities</a> - Taszk Lab, 2021</p><blockquote><p> <code>How to Tame Your Unicorn</code>BH议题中BootROM漏洞 OTA fix后的分析,探究华为的修复手法。</p></blockquote><ul><li> <a href="https://labs.taszk.io/blog/post/bootrom_head_resend/">CVE-2021-22434: Huawei Arbitrary Write in BootROM USB Stack</a></li><li> <a href="https://labs.taszk.io/blog/post/bootrom_usb/">CVE-2021-22429: Huawei Buffer Overflow in BootROM USB Stack</a></li></ul></li><li><p><a href="https://www.youtube.com/watch?v=0_E2NxpCAfw">Your Peripheral Has Planted Malware — An Exploit of NXP SOCs Vulnerability</a> - Yuwei ZHENG, Shaokun CAO, Yunding JIAN, Mingchuang QIN, Defcon26</p><blockquote><p> NXP SOC安全启动的错误实现导致可以打破信任链植入恶意程序</p></blockquote></li><li><p><a href="https://hardwear.io/usa-2019/presentations/Top-10-Secure-Boot-Mistakes-v1.1-hardwear-io-usa-2019-jasper-van-woudenberg.pdf">Top 10 Secure Boot mistakes</a> - Jasper van Woudenberg, hardware.io, 2019</p><blockquote><p> 这个算是一个总结性质的分享,总结了常见的安全启动的错误实现,已经相关的例子,对于BSP来说是个不错的参考材料?</p></blockquote></li><li><p><a href="https://raw.githubusercontent.com/windknown/presentations/master/Attack_Secure_Boot_of_SEP.pdf">Attack Secure Boot of SEP</a> - Xu Hao of Team Pangu, MOSEC, 2020</p><blockquote><p> TBD</p></blockquote></li><li><p><a href="https://i.blackhat.com/USA21/Wednesday-Handouts/us-21-Breaking-Secure-Bootloaders.pdf">Breaking Secure Bootloaders</a> Iskuri1, BH USA, 2021</p><blockquote><p> TBD</p></blockquote></li><li><p><a href="https://eshard.com/posts/pixel6_bootloader">eshared的pixel6_bootloader安全研究系列</a></p><blockquote><p> Pixel6修复了一系列bootloader的漏洞,作者通过bindiff找到,并深入研究了这些漏洞</p></blockquote></li></ul><h2 id="TZ相关"><a href="#TZ相关" class="headerlink" title="TZ相关"></a>TZ相关</h2><p>没看完的材料就是TBD的状态 :(</p><ul><li><p><a href="https://www.blackhat.com/docs/us-15/materials/us-15-Shen-Attacking-Your-Trusted-Core-Exploiting-Trustzone-On-Android.pdf">Attacking your “Trusted Core” Exploiting TrustZone on Android</a> - Di Shen (@returnsme), BH USA, 2015</p><blockquote><p> 华为Mate7的安全研究,从REE打到TEE</p></blockquote></li><li><p><a href="">Blue Pill for Your Phone</a> - Oleksandr Bazhaniuk & Yuriy Bulygin, BH USA, 2017</p><blockquote><p> Nexus&Pixel EL2的研究</p></blockquote></li><li><p><a href="https://i.blackhat.com/USA-19/Thursday/us-19-Peterlin-Breaking-Samsungs-ARM-TrustZone.pdf">BREAKING SAMSUNG’S ARM TRUSTZONE</a> - Maxime Peterlin & Alexandre Adamski & Joffrey Guilbon, BH USA, 2019</p><blockquote><p>TBD</p></blockquote></li><li><p><a href="">暗涌2020-小米5c中国产自研手机芯片澎湃S1</a> - Slipper, MOSEC, 2020</p><blockquote><p> 没找到Slide :( 只能结合<a href="https://evilpan.com/2020/07/25/mosec2020/">evilpan</a>的博客来理解了:) 一套fullchain exploit,从EL0一路打到S-EL1</p></blockquote></li></ul><h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><p>涉及底层的内容,也是上面学习上面内容的时候找到的,归类到这里 :)</p><ul><li><p><a href="https://blog.impalabs.com/2212_huawei-security-hypervisor.html">2212_huawei-security-hypervisor</a></p><blockquote><p> 详细地分析了华为的EL2实现,这篇详细到什么程度呢?我认为这是一篇生动形象的计算机体系结构课程 :) 非常值得阅读,全搞明白对ARM体系的理解要求很高。</p></blockquote></li><li><p><a href="https://blog.impalabs.com/2111_attacking-samsung-rkp.html">Attacking Samsung RKP</a></p><blockquote><p> TBD</p></blockquote></li><li><p><a href="https://blog.impalabs.com/2101_samsung-rkp-compendium.html">A Samsung RKP Compendium</a></p><blockquote><p> TBD</p></blockquote></li><li><p><a href="https://blog.impalabs.com/2212_advisory_huawei-secure-monitor.html">2212_advisory_huawei-secure-monitor</a></p><blockquote><p> 华为EL3 漏洞挖掘&利用,可以配合闻观行的 <code>EL3 Tour </code> 议题阅读</p></blockquote></li><li><p><a href="https://fredericb.info/archives.html">fred’s notes</a></p><blockquote><p> bootloader、security boot相关的博客都值得阅读</p></blockquote></li><li><p><a href="https://alephsecurity.com/2018/01/22/qualcomm-edl-1"><strong>Exploiting Qualcomm EDL Programmers</strong>系列</a></p><ul><li> 一共五篇,从网上泄漏的firehose开始研究,探究高通的安全启动、firehose功能,后面利用某些设备实现上的缺陷(开了secureboot的设备的firehose依然实现了peek、poke)实现内存读写,进而在不同设备上实现代码执行等操作。</li></ul></li><li><p><a href="https://blog.quarkslab.com/attacking-titan-m-with-only-one-byte.html">attacking-titan-m-with-only-one-byte</a></p><blockquote><p> TBD</p></blockquote></li></ul><h2 id="感想"><a href="#感想" class="headerlink" title="感想"></a>感想</h2><ol><li><p> 这些内容基本上都是围绕ARM架构做的安全研究,在学习的过程中会不自觉的拿optee来做对比,好让自己更容易理解这些内容 </p></li><li><p> 看了这些材料以及大佬分享的时间,这些研究真的太有意思了,我怎么没有早点看到 </p></li><li><p> 行业原因自然形成的壁垒,在做底层的研究的时候真的很明显,比如BootROM,如果有个手册的话…MTK那个BootROM我看过,这要没手册也太难分析了😭</p></li><li><p> 持续学习非常重要,正反馈让人觉得很爽 😄</p></li></ol><hr><p>恰好今天正好是2022年最后一天,转到IoT组也一年多了,能感受到自己在一点一点进步:</p><ul><li><p> 技术</p></li><li><p> 软技能(沟通协作、写文档)</p></li></ul><p>非常感谢玉伟对我的帮助和指导,在对本篇文章中资料的学习过程中,总能和之前我学习or工作中遇到的东西呼应起来,我也想起了和玉伟<code>one on one</code>的时候他给我讲学习方法、以及他个人是怎么做阅读的,醍醐灌顶来形容我现在的感受可能会比较恰当 :) </p><p><strong>希望23年可以进步更多一些,日拱一卒,功不唐捐。</strong></p>]]></content>
<summary type="html"><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p> 最近看了一些<code>Bootloader&amp;TZ</code>以及相关的议题,主要是ARM架构下的内容;正好这几个月我的<code>Leader</code>领着我们组一起学习<code>ARMv8&amp;v9</code>架构相关的知识,在阅读这些材料的时候给我提供了不少的帮助,让我理解起来更加容易,也算是变相检验学习成果咯。</p></summary>
<category term="阅读笔记" scheme="https://o0xmuhe.github.io/categories/%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/"/>
<category term="Bootloader" scheme="https://o0xmuhe.github.io/tags/Bootloader/"/>
<category term="TZ" scheme="https://o0xmuhe.github.io/tags/TZ/"/>
</entry>
<entry>
<title>议题学习:MOSEC2022 MediAttack - break the boot chain of MediaTek SoC</title>
<link href="https://o0xmuhe.github.io/2022/11/23/%E8%AE%AE%E9%A2%98%E8%A7%A3%E8%AF%BB-MOSEC2022-MediAttack-break-the-boot-chain-of-MediaTek-SoC/"/>
<id>https://o0xmuhe.github.io/2022/11/23/%E8%AE%AE%E9%A2%98%E8%A7%A3%E8%AF%BB-MOSEC2022-MediAttack-break-the-boot-chain-of-MediaTek-SoC/</id>
<published>2022-11-23T06:02:40.000Z</published>
<updated>2022-12-31T11:39:12.720Z</updated>
<content type="html"><![CDATA[<h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p> 前段时间MOSEC上盘古关于MTK BootROM Exploit的议题非常精彩,所以我画了一些时间对议题内容进行分析,并结合手边能找到的一些材料做了逆向分析,也感谢同事<a href="https://twitter.com/CossackWang">@C0ss4ck</a>在会场拍下了完整的Slide :) </p><span id="more"></span><p>配合MOSEC官方的微博食用更佳 :)</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221130141500497.png" alt="image-20221130141500497"></p><h2 id="议题学习"><a href="#议题学习" class="headerlink" title="议题学习"></a>议题学习</h2><h3 id="MTK-Based-Boot-flow"><a href="#MTK-Based-Boot-flow" class="headerlink" title="MTK Based Boot flow"></a>MTK Based Boot flow</h3><p>在进行研究之前需要搞明白MTK方案的设备的冷启动流程,议题中提供的图简洁明了:</p><table><thead><tr><th><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221123141303601.png" alt="image-20221123141303601"></th><th><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/mtk_boot.png" alt="boot_flow"></th></tr></thead></table><blockquote><p> 按照ARM的标准流程preloader应该是bl2</p></blockquote><p>因为后面使用了preloader的洞把BROM dump出来了,所以我判断MTK的preloader应该是和BROM跑在同一个Exception Level的,即EL3,后来也找了一些资料确认了这个说法,<strong>但是不确定现在最新的SoC还是不是这样的。</strong></p><h3 id="Preloader部分"><a href="#Preloader部分" class="headerlink" title="Preloader部分"></a>Preloader部分</h3><p> 出漏洞的模块在preloader的USB Download模式,MTK自定义了一些命令,在这个模式下USB handshake之后可以发送DA,然后加载DA,随后就可以和DA通信读写分区什么的,类似高通的9008(进edl模式后加载FH),当然如果开启了SecurityBoot,公版的DA无法使用,需要对应签名的DA才可以。</p><p> 根据大佬的议题内容可知,漏洞是一个整数溢出,是在判断读/写命令地址范围的时候出现的:</p><table><thead><tr><th><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221123142404496.png" alt="image-20221123142404496"></th><th><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221123142423007.png" alt="image-20221123142423007"></th></tr></thead></table><p>因为MTK的方案有很多开发板,所以基线代码基本上都很容易找到,比如使用了MT6737的香橙派-4G-IOT这个开发板(好像停产了,现存的巨贵),有个大哥把代码放github了</p><blockquote><p> <a href="https://github.com/SoCXin/MT6737/tree/master/linux">https://github.com/SoCXin/MT6737/tree/master/linux</a></p></blockquote><p>根据这份代码,分析这个漏洞其实很简单了</p><p><code>/home/muhe/Code/MT6737/linux/bootloader/preloader/platform/mt6735/src/core/download.c</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">usbdl_handler</span><span class="params">(struct bldr_comport *comport, u32 hshk_tmo_ms)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> u8 cmd;</span><br><span class="line"> u32 cnt = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (usbdl_check_start_command(comport, hshk_tmo_ms) == FALSE) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%s start cmd handshake timeout (%dms)\n"</span>, MOD, hshk_tmo_ms);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%s PASS Tool Sync Seq.\n"</span>, MOD);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* if log is disabled, re-init log port and enable it */</span></span><br><span class="line"> <span class="keyword">if</span> (comport->type == COM_USB && log_status() == <span class="number">0</span>) {</span><br><span class="line"> mtk_uart_init(UART_SRC_CLK_FRQ, CFG_LOG_BAUDRATE);</span><br><span class="line"> log_ctrl(<span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> dlcomport = comport;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (<span class="number">1</span>) {</span><br><span class="line"> platform_wdt_kick();</span><br><span class="line"></span><br><span class="line"> usbdl_get_byte(&cmd);</span><br><span class="line"> <span class="keyword">if</span> (cmd != CMD_GET_BL_VER)</span><br><span class="line"> usbdl_put_byte(cmd); <span class="comment">/* echo cmd */</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">switch</span> (cmd) {</span><br><span class="line"> <span class="keyword">case</span> CMD_GET_BL_VER:</span><br><span class="line"> ....</span><br><span class="line"> </span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> ...</span><br><span class="line"> </span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>支持的命令也很多:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221123142910268.png" alt="image-20221123142910268"></p><p>直接定位到 <code>static u32 usbdl_read16(bool legacy)</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> u32 <span class="title">usbdl_read16</span><span class="params">(<span class="keyword">bool</span> legacy)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> u32 index;</span><br><span class="line"> u32 base_addr=<span class="number">0</span>;</span><br><span class="line"> u32 len16=<span class="number">0</span>;</span><br><span class="line"> u32 len8=<span class="number">0</span>;</span><br><span class="line"> u16 data=<span class="number">0</span>;</span><br><span class="line"> u32 status=<span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> usbdl_get_dword(&base_addr); <span class="comment">// [1]获取地址</span></span><br><span class="line"> usbdl_put_dword(base_addr);</span><br><span class="line"></span><br><span class="line"> usbdl_get_dword(&len16); <span class="comment">// [2] 获取长度</span></span><br><span class="line"> usbdl_put_dword(len16);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* check addr alignment */</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="number">0</span> != (base_addr & (<span class="number">2</span><span class="number">-1</span>))) {</span><br><span class="line"> status = <span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">goto</span> end;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* check len */</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="number">0</span> == len16) {</span><br><span class="line"> status = <span class="number">-2</span>;</span><br><span class="line"> <span class="keyword">goto</span> end;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* convert half-word(2B) length to byte length */</span></span><br><span class="line"> len8 = (len16 << <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* overflow attack check */</span></span><br><span class="line"> <span class="keyword">if</span> (len16 >= len8) {</span><br><span class="line"> status = <span class="number">-3</span>;</span><br><span class="line"> <span class="keyword">goto</span> end;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* check if addr range is valid */</span></span><br><span class="line"> sec_region_check(base_addr,len8); <span class="comment">// [3] 安全检查</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!legacy) {</span><br><span class="line"> <span class="comment">/* return status */</span></span><br><span class="line"> usbdl_put_word(status);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (index = <span class="number">0</span>; index < len16; index++) { <span class="comment">// [4] 执行读操作并返回数据</span></span><br><span class="line"> data = *(u16*)(base_addr + (index << <span class="number">1</span>));</span><br><span class="line"> usbdl_put_word(data);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">end:</span><br><span class="line"> <span class="keyword">if</span>(!legacy) {</span><br><span class="line"> <span class="comment">/* return status */</span></span><br><span class="line"> usbdl_put_word(status);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> status;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>核心逻辑还是 <code>sec_region_check(base_addr,len8); </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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sec_region_check</span> <span class="params">(U32 addr, U32 len)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> U32 ret = SEC_OK;</span><br><span class="line"> U32 tmp = addr + len;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* check if it does access AHB/APB register */</span></span><br><span class="line"> <span class="keyword">if</span> ((IO_PHYS != (addr & REGION_MASK)) || (IO_PHYS != (tmp & REGION_MASK))) {</span><br><span class="line"> SMSG(<span class="string">"[%s] 0x%x Not AHB/APB Address\n"</span>, MOD, addr);</span><br><span class="line"> ASSERT(<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> (len >= REGION_BANK) {</span><br><span class="line"> SMSG(<span class="string">"[%s] Overflow\n"</span>,MOD);</span><br><span class="line"> ASSERT(<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> (blacklist_check(addr, len)) {</span><br><span class="line"> SMSG(<span class="string">"[%s] Not Allowed\n"</span>, MOD);</span><br><span class="line"> ASSERT(<span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> MTK_SECURITY_SW_SUPPORT</span></span><br><span class="line"> <span class="comment">/* check platform security region */</span></span><br><span class="line"> <span class="keyword">if</span> (SEC_OK != (ret = seclib_region_check(addr,len))) {</span><br><span class="line"> SMSG(<span class="string">"[%s] ERR '0x%x' ADDR: 0x%x, LEN: %d\n"</span>, MOD, ret, addr, len);</span><br><span class="line"> ASSERT(<span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></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><li> 这里可能是因为方案不同,大佬PPT里的那个方案是白名单的操作,只允许操作xxx,不过不影响理解。</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><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">REGION g_blacklist[] = {</span><br><span class="line"> {MSDC0_BASE, <span class="number">0x10000</span>},</span><br><span class="line"> {MSDC1_BASE, <span class="number">0x10000</span>},</span><br><span class="line"> {MSDC2_BASE, <span class="number">0x10000</span>},</span><br><span class="line"> {MSDC3_BASE, <span class="number">0x10000</span>},</span><br><span class="line"> {NFI_BASE, <span class="number">0x1000</span>},</span><br><span class="line"> {NFIECC_BASE, <span class="number">0x1000</span>},</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">blacklist_check</span><span class="params">(U32 addr, U32 len)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> ret = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> i = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> blacklist_size = <span class="keyword">sizeof</span>(g_blacklist) / <span class="keyword">sizeof</span>(REGION);</span><br><span class="line"> REGION region;</span><br><span class="line"> region.start = (<span class="keyword">unsigned</span> <span class="keyword">int</span>)addr;</span><br><span class="line"> region.size = (<span class="keyword">unsigned</span> <span class="keyword">int</span>)len;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (i = <span class="number">0</span>; i < blacklist_size; i++) {</span><br><span class="line"> <span class="keyword">if</span> (is_region_overlap(&region, &(g_blacklist[i]))) {</span><br><span class="line"> ret = <span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="title">is_region_overlap</span><span class="params">(REGION *region1, REGION *region2)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> overlap = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (region1->start + region1->size <= region2->start)</span><br><span class="line"> overlap = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (region2->start + region2->size <= region1->start)</span><br><span class="line"> overlap = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> overlap = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> overlap;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221123143717115.png" alt="image-20221123143717115"></p><blockquote><p> 这里就要祭出datasheet里的memory map</p></blockquote><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221123143410104.png" alt="image-20221123143410104"></p><p>根据memory map,利用这漏洞就可以把BROM dump出来了</p><h3 id="BROM部分"><a href="#BROM部分" class="headerlink" title="BROM部分"></a>BROM部分</h3><h4 id="基本分析"><a href="#基本分析" class="headerlink" title="基本分析"></a>基本分析</h4><p> MTK的话BROM Exp满天飞,多搜一搜可以找到,或者按照<a href="https://tinyhack.com/2021/01/31/dissecting-a-mediatek-bootrom-exploit/">dissecting-a-mediatek-bootrom-exploit</a>中的办法,应该也可以,或者对于没开SecurityBoot的设备搞个mini DA进去也可以(参考这里 <code>https://github.com/MTK-bypass/bypass_utility/blob/master/main.py#L111</code> )。</p><p>这里以某个SoC的BROM为例作分析,推荐使用Ghirda来做,选ARMv7就行。</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">DECIMAL HEXADECIMAL DESCRIPTION</span><br><span class="line">--------------------------------------------------------------------------------</span><br><span class="line">67676 0x1085C Mediatek bootloader</span><br><span class="line">72020 0x11954 SHA256 hash constants, little endian</span><br></pre></td></tr></table></figure><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221130133517456.png" alt="image-20221130133517456"></p><p>前面还是喜闻乐见的中断向量表,根据reset handler,能定位到类似main的位置,但是我们的目的是分析usb dl的逻辑,这里我看了下已知的文章,可以通过handshake来确定,直接暴搜一波 <code>A0 0A 50 05</code>,但是这里需要注意,有两个handshake,uart和usb的,需要做好区分,然后就可以定位到 process_cmd() 里了。</p><table><thead><tr><th><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221130133720683.png" alt="image-20221130133720683"></th><th><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221130133745944.png" alt="image-20221130133745944"></th></tr></thead></table><p>然后可以还原出来 相关标志位,如 security boot & SLA & DAA。</p><p>不过这显然不是这次的目的,这次是想找到盘古议题中提到的两个BROM的漏洞 :)</p><h4 id="议题中的漏洞"><a href="#议题中的漏洞" class="headerlink" title="议题中的漏洞"></a>议题中的漏洞</h4><h5 id="vuln1"><a href="#vuln1" class="headerlink" title="vuln1"></a>vuln1</h5><p>根据MTK的公告可知和议题内容,这个应该是那个Issue1,即 <code>Endpoint processing vulnerability</code> 的这枚漏洞 :)</p><p>我这里根据几个地方来确认函数位置的</p><ul><li> 少的可怜的两个字符串 <code>[USBDL]</code> 开头的,和timeout相关</li><li> 根据<code>https://github.com/chaosmaster/bypass_payloads</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> (*send_usb_response)(<span class="keyword">int</span>, <span class="keyword">int</span>, <span class="keyword">int</span>) = (<span class="keyword">void</span>*)******;</span><br><span class="line"><span class="keyword">int</span> (*(*usbdl_ptr))() = (<span class="keyword">void</span>*)******;</span><br><span class="line">*(<span class="keyword">volatile</span> <span class="keyword">uint32_t</span> *)(usbdl_ptr[<span class="number">0</span>] + <span class="number">8</span>) = (<span class="keyword">uint32_t</span>)usbdl_ptr[<span class="number">2</span>];</span><br><span class="line"><span class="keyword">void</span> (*usbdl_get_data)() = usbdl_ptr[<span class="number">1</span>];</span><br><span class="line"><span class="keyword">void</span> (*usbdl_put_data)() = usbdl_ptr[<span class="number">2</span>];</span><br><span class="line"><span class="keyword">void</span> (*usbdl_flush_data)() = usbdl_ptr[<span class="number">3</span>];</span><br></pre></td></tr></table></figure><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221124194300872.png" alt="image-20221124194300872"></p><ul><li> 议题中漏洞特征</li></ul><p>最终让我找到了这个漏洞,和我最开始预想的差不多,处理USB协议相关的逻辑,不过是在标准的流程后面</p><table><thead><tr><th><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221124194448445.png" alt="image-20221124194448445"></th><th><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221124194503152.png" alt="image-20221124194503152"></th></tr></thead></table><h5 id="TBD-vuln2"><a href="#TBD-vuln2" class="headerlink" title="[TBD]vuln2"></a>[TBD]vuln2</h5><p>说来也比较巧合,rrr拍的图里似乎没有标题为<code>MTK BootROM Vul #2</code> 的slide,所以我目前还没有分析出来,只找到了一些相关的资料辅助分析:</p><ul><li> <a href="https://www.usbzh.com/article/detail-842.html">https://www.usbzh.com/article/detail-842.html</a></li><li> <code>https://yhsnlkm.github.io/2019/08/14/USB相关/应用层遍历所有接入的USB设备-1/</code></li><li> <code>https://github.com/mtek-hack-hack/mtktest/blob/master/%20mtktest%20--username%20qq413187589/N65/N65_V1/usb/src/usbacm_drv.c</code></li><li> <a href="https://shequ.stmicroelectronics.cn/thread-612750-1-1.html">https://shequ.stmicroelectronics.cn/thread-612750-1-1.html</a></li></ul><p>比较有意思的是链接3里面的这份代码,看着很像古早时期的BROM源码 -.-</p><p>在usb相关的目录也找到了一些议题中提到的信息,比如CDC、<code>data_ep_in_info</code>,以及议题截图中一些变量命名,基本上都对的上,我猜测这应该是因为这是一种标准实现,所以延用这些命名方便分析,那么找洞的方向就有了:</p><ul><li> 继续了解USB CDC </li><li> 找一些标准实现看看,找一些特征+已知的USB相关的一些符号判断出来相关的处理逻辑大概在哪里</li><li> 结合MTK的公告描述来尝试找这个漏洞(<code>Character-formatting command vulnerability</code>)</li></ul><p>看了几个地方还不是很确定- -. 失败</p><h3 id="攻击思路"><a href="#攻击思路" class="headerlink" title="攻击思路"></a>攻击思路</h3><h4 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h4><ul><li> SLA (Serial Link Authorization): 未授权是没办法加载DA的</li><li> DAA (Download agent authentication): 对加载的DA做验证</li></ul><blockquote><p> 当然,如果能绕过SLA,加载自定义的DA,那DAA也是可以绕过的</p></blockquote><p>通过SP Flash Tool可以对设备进行读写</p><ul><li> Download-Agent: 一小段程序,加载到SRAM中和Host交互,类比高通的FH</li><li> Scatter: 可以理解成flash的内存布局,描述每个分区的情况,如起始地址、大小、属性等</li><li> Authentication File & Cert File: 开启了SecurityBoot的设备需要提供,用于验证DownloadAgent是否合法</li></ul><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221124195803394.png" alt="image-20221124195803394"></p><p>所以,对于开了SecurityBoot的设备,就不能用公版DA了,大佬的议题中也是以开了SecurityBoot的设备为例讲的,通过前面的漏洞disable sla & daa,从而实现加载自定义的DA,然后通过这个DA来读写任意分区,从而实现加载任意代码的目的 :)</p><h4 id="Attacking-DA"><a href="#Attacking-DA" class="headerlink" title="Attacking DA"></a>Attacking DA</h4><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221130231704274.png" alt="image-20221130231704274"></p><p>大佬在议题中对MTK的DA做了详细的介绍,这里主要涉及了</p><ul><li> DA如何被加载</li><li>DA的执行阶段<ul><li> stage1</li><li> stage2</li></ul></li><li> 如何攻击DA实现任意分区读写</li></ul><p>MTK的SP Flash Tool里带的这个公版DA其实是个DA的合集,SP FlashTool根据读到的chip id选对应的DA用来交互</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221208231817640.png" alt="image-20221208231817640"></p><h5 id="DA-stage1"><a href="#DA-stage1" class="headerlink" title="DA stage1"></a>DA stage1</h5><table><thead><tr><th><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221208231856888.png" alt="image-20221208231856888"></th><th><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221208231928842.png" alt="image-20221208231928842"></th></tr></thead></table><p>这里提到了一个EMI file,stage1会根据这个EMI file来初始化DRAM,既然可以从preloader里后去,那么前面的基线代码里妥妥也会有了</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221208231959205.png" alt="image-20221208231959205"></p><p>当然也可以借助工具来解析出来,比如这个 <code>https://github.com/mr-m96/MTKPreloaderParser</code>, 相关内容就不展开了,为了理解议题内容的话,只需要了解这个东西的作用以及在哪里就行了:)</p><h5 id="DA-stage2"><a href="#DA-stage2" class="headerlink" title="DA stage2"></a>DA stage2</h5><p>stage2是比较关键的内容了,它被stage1加载到了dram里执行(前面初始化dram这里要用)</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221208232056511.png" alt="image-20221208232056511"></p><p>这里列举了secure enable的情况,DA的能力将受到限制,即一部分功能无法使用,作者通过之前的BROM exploit disable了daa,然后加载自己patch过的da,从而使用这个patch过的da来实现全分区的读写,以及使用da中全部的功能。</p><h5 id="policy-part-map?"><a href="#policy-part-map?" class="headerlink" title="policy_part_map?"></a><strong>policy_part_map?</strong></h5><p>这部分感觉PPT顺序有点问题,不过也不是特别影响理解吧,主要是启动过程中对加载的镜像完整性校验相关的介绍,这块和后面大佬讲攻击流程能对上。</p><p>github随便搜了下,就能看明白这个东西了 :)</p><table><thead><tr><th><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221208232133500.png" alt="image-20221208232133500"></th><th><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221208232141320.png" alt="image-20221208232141320"></th></tr></thead></table><p>主要是有这么个结构体来描述对应的镜像的安全配置,是否受到保护、能不能刷这个分区等等啥的。</p><p>相关的部分代码,这是在加载镜像之前,加载这个policy,然后根据结果去对镜像做对应的操作,比如 是否应该做校验</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">char</span> <span class="title">get_sec_policy</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">int</span> policy_entry_idx)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">4<span class="keyword">unsigned</span> <span class="keyword">int</span> sboot_state = <span class="number">0</span>;</span><br><span class="line">4<span class="keyword">unsigned</span> <span class="keyword">int</span> lock_state = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">4<span class="keyword">unsigned</span> <span class="keyword">char</span> sec_policy = <span class="number">0</span>;</span><br><span class="line">4<span class="keyword">unsigned</span> <span class="keyword">int</span> ret = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">4ret = get_sec_state(&sboot_state, &lock_state);</span><br><span class="line">4<span class="comment">/* this API won't return error, so we don't process it here */</span></span><br><span class="line"></span><br><span class="line">4<span class="keyword">if</span> (sboot_state == <span class="number">0</span> && lock_state == LKS_UNLOCK)</span><br><span class="line">44sec_policy = g_policy_map[policy_entry_idx].sec_sbcdis_unlock_policy;</span><br><span class="line">4<span class="keyword">else</span> <span class="keyword">if</span> (sboot_state == <span class="number">0</span> && lock_state != LKS_UNLOCK)</span><br><span class="line">44sec_policy = g_policy_map[policy_entry_idx].sec_sbcdis_lock_policy;</span><br><span class="line">4<span class="keyword">else</span> <span class="keyword">if</span> (sboot_state == <span class="number">1</span> && lock_state == LKS_UNLOCK)</span><br><span class="line">44sec_policy = g_policy_map[policy_entry_idx].sec_sbcen_unlock_policy;</span><br><span class="line">4<span class="keyword">else</span> <span class="keyword">if</span> (sboot_state == <span class="number">1</span> && lock_state != LKS_UNLOCK)</span><br><span class="line">44sec_policy = g_policy_map[policy_entry_idx].sec_sbcen_lock_policy;</span><br><span class="line"></span><br><span class="line">4<span class="keyword">return</span> sec_policy;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="BROM-EXPLOIT"><a href="#BROM-EXPLOIT" class="headerlink" title="BROM EXPLOIT"></a>BROM EXPLOIT</h4><p>这里的话,参考<a href="https://tinyhack.com/2021/01/31/dissecting-a-mediatek-bootrom-exploit/">dissecting-a-mediatek-bootrom-exploit</a> 的介绍会了解的更清楚,简化一下描述就是:</p><ul><li><p>需要找到需要的函数、全局变量的地址</p><ul><li> <code>send_usb_response</code></li><li> <code>usbdl_put_dword</code></li><li> <code>usbdl_put_data</code></li><li> <code>usbdl_get_data</code></li><li> <code>uart_reg0</code></li><li> <code>uart_reg1</code></li><li> <code>sla_passed</code></li><li> <code>skip_auth_1</code></li><li> <code>skip_auth_2</code></li></ul></li><li><p>exp工作流程参考 <a href="https://github.com/MTK-bypass/exploit_common/blob/f158851273fec965f673af8647f01257fc9dea38/main.c">common exp</a>,类似议题中的Vuln1</p><p> <img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221130140148909.png" alt="image-20221130140148909"></p></li></ul><p>当然,所需要覆盖的变量也比较好找,把cmd是 <code>0xd8</code>的 <code>CMD_GET_TARGET_CONFIG</code>为入口就可以找到需要的东西了</p><table><thead><tr><th><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221130134354668.png" alt="image-20221130134354668"></th><th><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221130134410683.png" alt="image-20221130134410683"></th></tr></thead></table><h5 id="common-exp"><a href="#common-exp" class="headerlink" title="common exp"></a>common exp</h5><p>直接参考 <a href="https://github.com/MTK-bypass/exploit_common/blob/f158851273fec965f673af8647f01257fc9dea38/main.c">common exp</a>,就行,利用漏洞获得的任意地址读写能力去覆盖</p><ul><li> <code>sla_passed</code></li><li> <code>skip_auth_1</code></li><li> <code>skip_auth_2</code></li></ul><p>这三个变量,然后就可以加载任意da,并且禁用了daa</p><p><code>start.S</code> 直接跳main函数,里面逻辑也很简单,覆盖变量,然后接收下个阶段的交互(usb handshake),方便后续加载DA啥的,交互完毕,就正常进入usbdl模式去了</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></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> </span>{</span><br><span class="line"> send_usb_response(<span class="number">1</span>,<span class="number">0</span>,<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> print(<span class="string">"Sending pattern\n"</span>);</span><br><span class="line"> usbdl_put_dword(<span class="number">0xA1A2A3A4</span>);</span><br><span class="line"></span><br><span class="line"> *sla_passed = <span class="number">1</span>;</span><br><span class="line"> *skip_auth_1 = <span class="number">1</span>;</span><br><span class="line"> *skip_auth_2 = <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line"> print(<span class="string">"Waiting for handshake\n"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">char</span> sequence[] = {<span class="number">0xA0</span>, <span class="number">0x0A</span>, <span class="number">0x50</span>, <span class="number">0x05</span>};</span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">char</span> hs = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">uint32_t</span> i = <span class="number">0</span>; i < <span class="number">4</span>; i++, hs = <span class="number">0</span>) {</span><br><span class="line"> usbdl_get_data(&hs, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (sequence[i] == hs) {</span><br><span class="line"> hs = ~hs;</span><br><span class="line"> usbdl_put_data(&hs, <span class="number">1</span>);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> i = <span class="number">0</span>;</span><br><span class="line"> print(<span class="string">"Handshake failed\n"</span>);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> print(<span class="string">"Handshake..\n"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> print(<span class="string">"Handshake completed\n"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="MTE-mode"><a href="#MTE-mode" class="headerlink" title="MTE mode"></a>MTE mode</h4><p>这个模式看描述是MTK的一个特殊的测试模式,也算是一个之前没见过的攻击面</p><table><thead><tr><th><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221124201408856.png" alt="image-20221124201408856"></th><th><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221124222151110.png" alt="image-20221124222151110"></th></tr></thead></table><p>在这个模式下,可以做很多事情:</p><ul><li> Obtain/Modify EFUSE/RPMB Info </li><li> Load Customized OS </li><li> USERDATA Decrypt</li><li> Obtain/Modify Hardware Key </li><li> Unlock Bootloader</li><li> …</li></ul><p>巧了,咱手里正好有个某个MTK方案的设备的完整镜像 :-) 根据PPT中的信息,可以check下相关的逻辑</p><table><thead><tr><th><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221124210054040.png" alt="image-20221124210054040"></th><th><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221124221837757.png" alt="image-20221124221837757"></th></tr></thead></table><p>我这个设备没有找到相关的逻辑,应该是删除了这个模式,不过幸运的是 <code>meta_tst</code> 没有删除:),而且根据PPT里的内容,这个服务应该是比较核心的,MTK设计了私有协议做一些交互</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221124212759107.png" alt="image-20221124212759107"></p><p>分析的难度也不大,而且有趣的是如果你在github上搜一些特定的字符串,会发现很多有意思的repo :) 这对理解一些逻辑很有帮助</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221124221704268.png" alt="image-20221124221704268"></p><h4 id="more-exploit"><a href="#more-exploit" class="headerlink" title="more exploit"></a>more exploit</h4><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221208232231058.png" alt="image-20221208232231058"></p><p>这没什么可说的,既然从源头破坏掉了信任链,那么自然可以做任何事 😎</p><p>基本上一些很成熟的“取证”工具都能干- 。- 比如这一篇</p><p><a href="https://blog.oxygen-forensic.com/support-for-mediatek-devices-in-oxygen-forensic-detective/">support-for-mediatek-devices-in-oxygen-forensic-detective</a></p><p>感兴趣的话可以阅读一下</p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p> 这次虽然过程艰辛又带着一些遗憾,不过个人起码了解了MTK方案BROM Exploit的思路,vuln#2还没找到,后面等不忙了时间多了再尝试看看好了 :) </p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://github.com/SoCXin/MT6737/tree/master/linux">https://github.com/SoCXin/MT6737/tree/master/linux</a></p><p><a href="https://github.com/chaosmaster/bypass_payloads">https://github.com/chaosmaster/bypass_payloads</a></p><p><a href="https://tinyhack.com/2021/01/31/dissecting-a-mediatek-bootrom-exploit/">https://tinyhack.com/2021/01/31/dissecting-a-mediatek-bootrom-exploit/</a></p><p><a href="https://www.cnblogs.com/wen123456/p/14034493.html">https://www.cnblogs.com/wen123456/p/14034493.html</a></p><p><a href="https://blog.csdn.net/u011784994/article/details/104898430">https://blog.csdn.net/u011784994/article/details/104898430</a></p><p><a href="https://github.com/rn2/ven/blob/db95d7f096/hardware/meta/common/README">https://github.com/rn2/ven/blob/db95d7f096/hardware/meta/common/README</a></p><p><a href="https://blog.oxygen-forensic.com/support-for-mediatek-devices-in-oxygen-forensic-detective/">https://blog.oxygen-forensic.com/support-for-mediatek-devices-in-oxygen-forensic-detective/</a></p>]]></content>
<summary type="html"><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p> 前段时间MOSEC上盘古关于MTK BootROM Exploit的议题非常精彩,所以我画了一些时间对议题内容进行分析,并结合手边能找到的一些材料做了逆向分析,也感谢同事<a href="https://twitter.com/CossackWang">@C0ss4ck</a>在会场拍下了完整的Slide :) </p></summary>
<category term="Hackcon" scheme="https://o0xmuhe.github.io/categories/Hackcon/"/>
<category term="MTK" scheme="https://o0xmuhe.github.io/tags/MTK/"/>
<category term="preloader" scheme="https://o0xmuhe.github.io/tags/preloader/"/>
<category term="BootROM" scheme="https://o0xmuhe.github.io/tags/BootROM/"/>
</entry>
<entry>
<title>Qual+Android方案Unlock学习 以Oneplus7Pro为例</title>
<link href="https://o0xmuhe.github.io/2022/11/01/Qual-Android%E6%96%B9%E6%A1%88Unlock%E5%AD%A6%E4%B9%A0-%E4%BB%A5Oneplus7Pro%E4%B8%BA%E4%BE%8B/"/>
<id>https://o0xmuhe.github.io/2022/11/01/Qual-Android%E6%96%B9%E6%A1%88Unlock%E5%AD%A6%E4%B9%A0-%E4%BB%A5Oneplus7Pro%E4%B8%BA%E4%BE%8B/</id>
<published>2022-11-01T14:56:50.000Z</published>
<updated>2022-11-13T07:59:32.397Z</updated>
<content type="html"><![CDATA[<h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p> 严格意义上来说本文应该叫做: <<我本来只是想救个砖,但是却逆向了刷机工具尝试搞清楚<code>android unlock</code>的原理>> :D </p><span id="more"></span><p>前段时间因为一些工作需求想给手里的测试机(一加7Pro)刷个ColorOS,因为之前想体验Android12,机器是刷了个userdebug的lineageos,遂尝试了卡刷、sideload等之后机器被我搞坏了,开机直接recovery,报错信息是什么 mount fs的时候失败了 :( 没办法只能救转了,逛了一圈论坛发现有人提供<a href="https://www.oneplusbbs.com/forum.php?mod=viewthread&tid=4730052">9008刷机工具</a>,通过万能的9008救回来之后,我就想做点别的: 把他的firehose“偷”出来玩玩。</p><h2 id="Unpack-guacamole-21-H-04-190416-ops"><a href="#Unpack-guacamole-21-H-04-190416-ops" class="headerlink" title="Unpack guacamole_21_H.04_190416.ops"></a>Unpack guacamole_21_H.04_190416.ops</h2><p>刷机工具解压之后就几个文件,一个刷机工具 msmdownloadtoolv4.0.88,还有个<code>guacamole_21_H.04_190416.ops</code>,一看就是固件包,然后就是一些完整性校验用的文件。</p><p>根据经验,这类刷机包里应该是内置了firehouse的,可以考虑两条路:</p><ol><li> 解包,直接把firehose提出来</li><li> 内存dump,在刷机工具尝试给手机传输firehose的时候的时候内存dump,从内存里根据ELF文件头给截出来</li></ol><p>方法2是我最开始尝试的办法,但是dump了几次,发现了好几个ELF,但是都不对,所以尝试方法1 :D 很显然这个包是厂商自己搞得加密,不过网上已经有大佬分析了(早用方法1就少走弯路了),所以根据 <a href="https://www.droidwin.com/how-to-extract-oneplus-ops-firmware/">How to Extract/Decrypt OnePlus OPS Firmware</a> 提供的工具,可以成功吧固件包解开,获取到firehose</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"><span class="comment"># muhe @ muheMacBookAir in ~/Work/play_with_oneplus7pro on git:main x [22:01:37]</span></span><br><span class="line">$ ls -al prog_firehose_*</span><br><span class="line">-rw-r--r--@ 1 muhe staff 726400 Oct 28 22:46 prog_firehose_ddr.elf</span><br><span class="line">-rw-r--r--@ 1 muhe staff 726272 Oct 28 22:46 prog_firehose_lite.elf</span><br></pre></td></tr></table></figure><p>随便试了一把读分区,是可以的,说明firehose是没问题的 :)</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221102220307918.png" alt="image-20221102220307918"></p><p>然后就想着顺手看点别的,研究研究Qual+Android平台的解锁BL是怎么实现的,遂有了后续的过程。</p><h2 id="Unlock探究"><a href="#Unlock探究" class="headerlink" title="Unlock探究"></a>Unlock探究</h2><h3 id="开发者选项–允许解锁"><a href="#开发者选项–允许解锁" class="headerlink" title="开发者选项–允许解锁"></a>开发者选项–允许解锁</h3><blockquote><p> 参考android-9-r1, 因为现在用的一加的系统的是Android9的</p></blockquote><blockquote><p> 没在开发这里允许解锁BL的话,直接fastboot oem unlock是不行的</p></blockquote><ol><li> onOemUnlockConfirmed</li></ol><p><a href="https://cs.android.com/android/platform/superproject/+/android-9.0.0_r1:packages/apps/Settings/src/com/android/settings/development/OemUnlockPreferenceController.java;l=132">https://cs.android.com/android/platform/superproject/+/android-9.0.0_r1:packages/apps/Settings/src/com/android/settings/development/OemUnlockPreferenceController.java;l=132</a></p><figure class="highlight java"><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">public</span> <span class="keyword">void</span> <span class="title">onOemUnlockConfirmed</span><span class="params">()</span> </span>{</span><br><span class="line"> mOemLockManager.setOemUnlockAllowedByUser(<span class="keyword">true</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol start="2"><li> setOemUnlockAllowedByUser</li></ol><p><a href="https://cs.android.com/android/platform/superproject/+/android-9.0.0_r1:frameworks/base/core/java/android/service/oemlock/OemLockManager.java;drc=b45a2ea782074944f79fc388df20b06e01f265f7;l=114">https://cs.android.com/android/platform/superproject/+/android-9.0.0_r1:frameworks/base/core/java/android/service/oemlock/OemLockManager.java;drc=b45a2ea782074944f79fc388df20b06e01f265f7;l=114</a></p><figure class="highlight java"><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">@RequiresPermission(android.Manifest.permission.MANAGE_USER_OEM_UNLOCK_STATE)</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setOemUnlockAllowedByUser</span><span class="params">(<span class="keyword">boolean</span> allowed)</span> </span>{</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> mService.setOemUnlockAllowedByUser(allowed);</span><br><span class="line"> } <span class="keyword">catch</span> (RemoteException e) {</span><br><span class="line"> <span class="keyword">throw</span> e.rethrowFromSystemServer();</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><ol start="3"><li> setOemUnlockAllowedByUser</li></ol><p><a href="https://cs.android.com/android/platform/superproject/+/android-9.0.0_r1:frameworks/base/services/core/java/com/android/server/oemlock/OemLockService.java;l=156;drc=b45a2ea782074944f79fc388df20b06e01f265f7;bpv=0;bpt=1">https://cs.android.com/android/platform/superproject/+/android-9.0.0_r1:frameworks/base/services/core/java/com/android/server/oemlock/OemLockService.java;l=156;drc=b45a2ea782074944f79fc388df20b06e01f265f7;bpv=0;bpt=1</a></p><figure class="highlight java"><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">// The user has the final say so if they allow unlock, then the device allows the bootloader</span></span><br><span class="line"><span class="comment">// to OEM unlock it.</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setOemUnlockAllowedByUser</span><span class="params">(<span class="keyword">boolean</span> allowedByUser)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (ActivityManager.isUserAMonkey()) {</span><br><span class="line"> <span class="comment">// Prevent a monkey from changing this</span></span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> enforceManageUserOemUnlockPermission();</span><br><span class="line"> enforceUserIsAdmin();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">long</span> token = Binder.clearCallingIdentity();</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">if</span> (!isOemUnlockAllowedByAdmin()) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> SecurityException(<span class="string">"Admin does not allow OEM unlock"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!mOemLock.isOemUnlockAllowedByCarrier()) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> SecurityException(<span class="string">"Carrier does not allow OEM unlock"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> mOemLock.setOemUnlockAllowedByDevice(allowedByUser);</span><br><span class="line"> setPersistentDataBlockOemUnlockAllowedBit(allowedByUser);</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> Binder.restoreCallingIdentity(token);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol start="4"><li> setPersistentDataBlockOemUnlockAllowedBit</li></ol><p><a href="https://cs.android.com/android/platform/superproject/+/android-9.0.0_r1:frameworks/base/services/core/java/com/android/server/oemlock/OemLockService.java;drc=b45a2ea782074944f79fc388df20b06e01f265f7;bpv=0;bpt=1;l=232">https://cs.android.com/android/platform/superproject/+/android-9.0.0_r1:frameworks/base/services/core/java/com/android/server/oemlock/OemLockService.java;drc=b45a2ea782074944f79fc388df20b06e01f265f7;bpv=0;bpt=1;l=232</a></p><figure class="highlight java"><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="comment">/**</span></span><br><span class="line"><span class="comment"> * Always synchronize the OemUnlockAllowed bit to the FRP partition, which</span></span><br><span class="line"><span class="comment"> * is used to erase FRP information on a unlockable device.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">setPersistentDataBlockOemUnlockAllowedBit</span><span class="params">(<span class="keyword">boolean</span> allowed)</span> </span>{</span><br><span class="line"> <span class="keyword">final</span> PersistentDataBlockManagerInternal pdbmi</span><br><span class="line"> = LocalServices.getService(PersistentDataBlockManagerInternal.class);</span><br><span class="line"> <span class="comment">// if mOemLock is PersistentDataBlockLock, then the bit should have already been set</span></span><br><span class="line"> <span class="keyword">if</span> (pdbmi != <span class="keyword">null</span> && !(mOemLock <span class="keyword">instanceof</span> PersistentDataBlockLock)) {</span><br><span class="line"> Slog.i(TAG, <span class="string">"Update OEM Unlock bit in pst partition to "</span> + allowed);</span><br><span class="line"> pdbmi.forceOemUnlockEnabled(allowed);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol start="5"><li> pdbmi.forceOemUnlockEnabled(allowed);</li></ol><p><a href="https://cs.android.com/android/platform/superproject/+/android-9.0.0_r1:frameworks/base/services/core/java/com/android/server/PersistentDataBlockService.java;l=677;bpv=0;bpt=1">https://cs.android.com/android/platform/superproject/+/android-9.0.0_r1:frameworks/base/services/core/java/com/android/server/PersistentDataBlockService.java;l=677;bpv=0;bpt=1</a></p><figure class="highlight java"><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="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">forceOemUnlockEnabled</span><span class="params">(<span class="keyword">boolean</span> enabled)</span> </span>{</span><br><span class="line"> <span class="keyword">synchronized</span> (mLock) {</span><br><span class="line"> doSetOemUnlockEnabledLocked(enabled);</span><br><span class="line"> computeAndWriteDigestLocked();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol start="6"><li> doSetOemUnlockEnabledLocked</li></ol><p><a href="https://cs.android.com/android/platform/superproject/+/android-9.0.0_r1:frameworks/base/services/core/java/com/android/server/PersistentDataBlockService.java;drc=b45a2ea782074944f79fc388df20b06e01f265f7;bpv=0;bpt=1;l=421">https://cs.android.com/android/platform/superproject/+/android-9.0.0_r1:frameworks/base/services/core/java/com/android/server/PersistentDataBlockService.java;drc=b45a2ea782074944f79fc388df20b06e01f265f7;bpv=0;bpt=1;l=421</a></p><figure class="highlight java"><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"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">doSetOemUnlockEnabledLocked</span><span class="params">(<span class="keyword">boolean</span> enabled)</span> </span>{</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> FileChannel channel = getBlockOutputChannel();</span><br><span class="line"> </span><br><span class="line"> channel.position(getBlockDeviceSize() - <span class="number">1</span>);</span><br><span class="line"> </span><br><span class="line"> ByteBuffer data = ByteBuffer.allocate(<span class="number">1</span>);</span><br><span class="line"> data.put(enabled ? (<span class="keyword">byte</span>) <span class="number">1</span> : (<span class="keyword">byte</span>) <span class="number">0</span>);</span><br><span class="line"> data.flip();</span><br><span class="line"> channel.write(data);</span><br><span class="line"> channel.force(<span class="keyword">true</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (IOException e) {</span><br><span class="line"> Slog.e(TAG, <span class="string">"unable to access persistent partition"</span>, e);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> SystemProperties.set(OEM_UNLOCK_PROP, enabled ? <span class="string">"1"</span> : <span class="string">"0"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>设备文件的某个位置写1,看起来是修改配置了</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String PERSISTENT_DATA_BLOCK_PROP = <span class="string">"ro.frp.pst"</span>;</span><br></pre></td></tr></table></figure><p>在一加上看是 :</p><figure class="highlight java"><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="number">130</span>|OnePlus7Pro:/ $ getprop ro.frp.pst</span><br><span class="line">/dev/block/bootdevice/by-name/config</span><br></pre></td></tr></table></figure><p>那么操作就是写这个分区了,把enbale标志位写进去,尝试进edl把config读出来看看</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">PS C:\Users\Admin> adb reboot edl</span><br><span class="line">PS C:\Users\Admin></span><br></pre></td></tr></table></figure><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221102220543055.png" alt="image-20221102220543055"></p><p>设置了这个标志位之后:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221102220559725.png" alt="image-20221102220559725"></p><p>发现设备已经是允许unlock操作了(这里的允许是允许你去 <code>fastboot oem unlock</code>)</p><h3 id="fastboot-oem-unlock"><a href="#fastboot-oem-unlock" class="headerlink" title="fastboot oem unlock"></a>fastboot oem unlock</h3><h4 id="extract-LinuxLoader-from-abl"><a href="#extract-LinuxLoader-from-abl" class="headerlink" title="extract LinuxLoader from abl"></a>extract LinuxLoader from abl</h4><ol><li><p> 把<code>UEFI PI Firmware Volume</code>从abl.elf里切出来</p></li><li><p> uefi-firmware-parser 解析</p></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><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">$ uefi-firmware-parser -e test</span><br><span class="line">/usr/local/bin/uefi-firmware-parser:38: SyntaxWarning: "is not" with a literal. Did you mean "!="?</span><br><span class="line"> if parser.type() is not 'unknown':</span><br><span class="line">/usr/local/bin/uefi-firmware-parser:141: SyntaxWarning: "is" with a literal. Did you mean "=="?</span><br><span class="line"> if parser.type() is 'unknown':</span><br><span class="line">Firmware Volume: 8c8ce578-8a3d-4f1c-9935-896185c32dd3 attr 0x0003feff, rev 2, cksum 0xd3be, size 0x22000 (139264 bytes)</span><br><span class="line"> Firmware Volume Blocks: (272, 0x200)</span><br><span class="line"> File 0: 9e21fd93-9c72-4c15-8c4b-e77f1db2d792 type 0x0b, attr 0x00, state 0x07, size 0x204c2 (132290 bytes), (firmware volume image)</span><br><span class="line"> Section 0: type 0x02, size 0x204aa (132266 bytes) (Guid Defined section)</span><br><span class="line"> Guid-Defined: ee4e5898-3914-4259-9d6e-dc7bd79403cf offset= 0x18 attrs= 0x1 (PROCESSING_REQUIRED)</span><br><span class="line"> Section 0: type 0x19, size 0x4 (4 bytes) (Raw section)</span><br><span class="line"> Section 1: type 0x17, size 0x6d0c4 (446660 bytes) (Firmware volume image section)</span><br><span class="line"> Firmware Volume: 8c8ce578-8a3d-4f1c-9935-896185c32dd3 attr 0x0003feff, rev 2, cksum 0xa27, size 0x6d0c0 (446656 bytes)</span><br><span class="line"> Firmware Volume Blocks: (6979, 0x40)</span><br><span class="line"> File 0: ffffffff-ffff-ffff-ffff-ffffffffffff type 0xf0, attr 0x00, state 0x07, size 0x2c (44 bytes), (ffs padding)</span><br><span class="line"> File 1: f536d559-459f-48fa-8bbc-43b554ecae8d type 0x09, attr 0x00, state 0x07, size 0x6d038 (446520 bytes), (application)</span><br><span class="line"> Section 0: type 0x15, size 0x1c (28 bytes) (User interface name section)</span><br><span class="line"> Name: LinuxLoader</span><br><span class="line"> Section 1: type 0x10, size 0x6d004 (446468 bytes) (PE32 image section)</span><br><span class="line">Dumping...</span><br><span class="line">Wrote: ./volume-0.fv</span><br><span class="line">Wrote: ./volume-0/filesystem.ffs</span><br><span class="line">Wrote: ./volume-0/file-9e21fd93-9c72-4c15-8c4b-e77f1db2d792/file.obj</span><br><span class="line">Wrote: ./volume-0/file-9e21fd93-9c72-4c15-8c4b-e77f1db2d792/section0.guid</span><br><span class="line">Wrote: ./volume-0/file-9e21fd93-9c72-4c15-8c4b-e77f1db2d792/section0/section0.raw</span><br><span class="line">Wrote: ./volume-0/file-9e21fd93-9c72-4c15-8c4b-e77f1db2d792/section0/section1.fv</span><br><span class="line">Wrote: ./volume-0/file-9e21fd93-9c72-4c15-8c4b-e77f1db2d792/section0/section1/volume-ee4e5898-3914-4259-9d6e-dc7bd79403cf.fv</span><br><span class="line">Wrote: ./volume-0/file-9e21fd93-9c72-4c15-8c4b-e77f1db2d792/section0/section1/volume-ee4e5898-3914-4259-9d6e-dc7bd79403cf/filesystem.ffs</span><br><span class="line">Wrote: ./volume-0/file-9e21fd93-9c72-4c15-8c4b-e77f1db2d792/section0/section1/volume-ee4e5898-3914-4259-9d6e-dc7bd79403cf/file-ffffffff-ffff-ffff-ffff-ffffffffffff/file.obj</span><br><span class="line">Wrote: ./volume-0/file-9e21fd93-9c72-4c15-8c4b-e77f1db2d792/section0/section1/volume-ee4e5898-3914-4259-9d6e-dc7bd79403cf/file-f536d559-459f-48fa-8bbc-43b554ecae8d/file.obj</span><br><span class="line">Wrote: ./volume-0/file-9e21fd93-9c72-4c15-8c4b-e77f1db2d792/section0/section1/volume-ee4e5898-3914-4259-9d6e-dc7bd79403cf/file-f536d559-459f-48fa-8bbc-43b554ecae8d/section0.ui</span><br><span class="line">Wrote: ./volume-0/file-9e21fd93-9c72-4c15-8c4b-e77f1db2d792/section0/section1/volume-ee4e5898-3914-4259-9d6e-dc7bd79403cf/file-f536d559-459f-48fa-8bbc-43b554ecae8d/section1.pe</span><br><span class="line">Wrote: ./volume-0/file-9e21fd93-9c72-4c15-8c4b-e77f1db2d792/section0/guided.preamble</span><br><span class="line">Wrote: ./volume-0/file-9e21fd93-9c72-4c15-8c4b-e77f1db2d792/section0/guided.certs</span><br></pre></td></tr></table></figure><p><code>./volume-0/file-9e21fd93-9c72-4c15-8c4b-e77f1db2d792/section0/section1/volume-ee4e5898-3914-4259-9d6e-dc7bd79403cf/file-f536d559-459f-48fa-8bbc-43b554ecae8d/section1.pe </code> 就是我们需要的LinuxLoader</p><h4 id="过程分析"><a href="#过程分析" class="headerlink" title="过程分析"></a>过程分析</h4><blockquote><p> 这算是UEFI的一个应用程序</p></blockquote><p>然后就去找 fastboot oem unlock 对应的处理逻辑 : </p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221102221112067.png" alt="image-20221102221112067"></p><p>如果没有在设置-开发者选项中点击允许解锁BL,直接oem unlock是不行的,</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221102221127781.png" alt="image-20221102221127781"></p><p>在abl中也找到了对应的报错信息:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221102221145363.png" alt="image-20221102221145363"></p><p>这里没符号不太好理解,要么找edk2的源码做参考辅助分析,要么某个基线代码build一份带符号的abl出来,这里因为没有在设置中 “允许解锁boot loader” 所以可以结合上面AOSP中的代码做辅助分析。</p><p>看看LinuxLoader的源码会更好理解</p><p>这里其实是判断了两个标志位:</p><ul><li> unlock: 解锁,不验证BL了</li><li> unlock_critical: 设置后了之后才能刷对应的敏感分区</li></ul><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221102221215657.png" alt="image-20221102221215657"></p><p>edk2开源实现中默认的保护分区,这个是可以修改的</p><p>结合LinuxLoader的源码,网上可以找到一些leak的实现,能用于辅助分析</p><p>在入口 <code>LinuxLoaderEntry</code> 开始的时候,会初始化一个Deviceinfo的结构体</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="comment">// Initialize verified boot & Read Device Info</span></span><br><span class="line">Status = DeviceInfoInit ();</span><br><span class="line"><span class="keyword">if</span> (Status != EFI_SUCCESS) {</span><br><span class="line"> DEBUG ((EFI_D_ERROR, <span class="string">"Initialize the device info failed: %r\\n"</span>, Status));</span><br><span class="line"> <span class="keyword">goto</span> stack_guard_update_default;</span><br><span class="line">}</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><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> DEVICE_MAGIC <span class="meta-string">"ANDROID-BOOT!"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> DEVICE_MAGIC_SIZE 13</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAX_VERSION_LEN 64</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAX_VB_PARTITIONS 32</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAX_USER_KEY_SIZE 2048</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">enum</span> <span class="title">unlock_type</span> {</span></span><br><span class="line"> UNLOCK = <span class="number">0</span>,</span><br><span class="line"> UNLOCK_CRITICAL,</span><br><span class="line">};</span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">device_info</span> {</span></span><br><span class="line"> CHAR8 magic[DEVICE_MAGIC_SIZE];</span><br><span class="line"> BOOLEAN is_unlocked;</span><br><span class="line"> BOOLEAN is_unlock_critical;</span><br><span class="line"> BOOLEAN is_charger_screen_enabled;</span><br><span class="line"> CHAR8 bootloader_version[MAX_VERSION_LEN];</span><br><span class="line"> CHAR8 radio_version[MAX_VERSION_LEN];</span><br><span class="line"> BOOLEAN verity_mode; <span class="comment">// TRUE = enforcing, FALSE = logging</span></span><br><span class="line"> UINT32 user_public_key_length;</span><br><span class="line"> CHAR8 user_public_key[MAX_USER_KEY_SIZE];</span><br><span class="line"> UINT64 rollback_index[MAX_VB_PARTITIONS];</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">usb_composition</span> <span class="title">usb_comp</span>;</span></span><br><span class="line">} DeviceInfo;</span><br><span class="line"><span class="function">EFI_STATUS</span></span><br><span class="line"><span class="function"><span class="title">ReadWriteDeviceInfo</span> <span class="params">(<span class="keyword">vb_device_state_op_t</span> Mode, <span class="keyword">void</span> *DevInfo, UINT32 Sz)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> EFI_STATUS Status = EFI_INVALID_PARAMETER;</span><br><span class="line"> QCOM_VERIFIEDBOOT_PROTOCOL *VbIntf;</span><br><span class="line"></span><br><span class="line"> Status = gBS->LocateProtocol (&gEfiQcomVerifiedBootProtocolGuid, <span class="literal">NULL</span>,</span><br><span class="line"> (VOID **)&VbIntf);</span><br><span class="line"> <span class="keyword">if</span> (Status != EFI_SUCCESS) {</span><br><span class="line"> DEBUG ((EFI_D_ERROR, <span class="string">"Unable to locate VB protocol: %r\\n"</span>, Status));</span><br><span class="line"> <span class="keyword">return</span> Status;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> Status = VbIntf->VBRwDeviceState (VbIntf, Mode, DevInfo, Sz);</span><br><span class="line"> <span class="keyword">if</span> (Status != EFI_SUCCESS) {</span><br><span class="line"> DEBUG ((EFI_D_ERROR, <span class="string">"VBRwDevice failed with: %r\\n"</span>, Status));</span><br><span class="line"> <span class="keyword">return</span> Status;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> Status;</span><br><span class="line">}</span><br><span class="line"># VerifiedBoot Protocol</span><br><span class="line">gEfiQcomVerifiedBootProtocolGuid = { <span class="number">0x8e5eff91</span>, <span class="number">0x21b6</span>, <span class="number">0x47d3</span>, { <span class="number">0xaf</span>, <span class="number">0x2b</span>, <span class="number">0xc1</span>, <span class="number">0x5a</span>, <span class="number">0x1</span>, <span class="number">0xe0</span>, <span class="number">0x20</span>, <span class="number">0xec</span> } }</span><br></pre></td></tr></table></figure><p>可以根据这个<code>gEfiQcomVerifiedBootProtocol</code> 去基线中搜到对应的实现,这里就无法展示了。</p><p>结合利用FH读出来的devinfo分区:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221102221321664.png" alt="image-20221102221321664"></p><p>看来</p><figure class="highlight jsx"><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">BOOLEAN is_unlocked;</span><br><span class="line">BOOLEAN is_unlock_critical;</span><br></pre></td></tr></table></figure><p>都是0,这个和目前未解锁的状态是符合的。</p><h2 id="FH读写分区"><a href="#FH读写分区" class="headerlink" title="FH读写分区"></a>FH读写分区</h2><p>这里我本来想用QFIL的,但是一加的firehose显然是自己改过的,只能读,写的话有个认证token,所以考虑了开源实现 <a href="https://github.com/bkerler/edl">edl</a>,这个工具我发现对xiaomi和oneplus有支持,</p><p>就在我想着能一把梭实现 <code>r/w</code>的时候,悲剧发生了 :</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><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># jiazhenjie @ mbp in ~/tools/edl on git:01f84bf o [16:54:13] C:1</span></span><br><span class="line">$ python3 edl.py w devinfo /Users/jiazhenjie/Downloads/devinfo.bin --loader=/Users/jiazhenjie/Downloads/prog_firehose_ddr.elf --memory=UFS --lun=4</span><br><span class="line">Qualcomm Sahara / Firehose Client V3.52 (c) B.Kerler 2018-2021.</span><br><span class="line">main - Using loader /Users/jiazhenjie/Downloads/prog_firehose_ddr.elf ...</span><br><span class="line">main - Waiting <span class="keyword">for</span> the device</span><br><span class="line">main - Device detected :)</span><br><span class="line">main - Mode detected: firehose</span><br><span class="line">firehose - Chip serial num: 2360036966 (0x8cab4e66)</span><br><span class="line">firehose - Supported Functions: program,<span class="built_in">read</span>,nop,patch,configure,setbootablestoragedrive,erase,power,firmwarewrite,getstorageinfo,benchmark,emmc,ufs,fixgpt,getsha256digest,gethwversion,getrfversion,getprjversion,setprojmodel,sha256init,sha256final</span><br><span class="line">firehose -</span><br><span class="line">firehose</span><br><span class="line">firehose - [LIB]: Couldn<span class="string">'t detect MaxPayloadSizeFromTargetinBytes</span></span><br><span class="line"><span class="string">firehose</span></span><br><span class="line"><span class="string">firehose - [LIB]: Couldn'</span>t detect TargetName</span><br><span class="line">firehose - TargetName=Unknown</span><br><span class="line">firehose - MemoryName=UFS</span><br><span class="line">firehose - Version=1</span><br><span class="line">firehose_client - Supported <span class="built_in">functions</span>:</span><br><span class="line">-----------------</span><br><span class="line">program,<span class="built_in">read</span>,nop,patch,configure,setbootablestoragedrive,erase,power,firmwarewrite,getstorageinfo,benchmark,emmc,ufs,fixgpt,getsha256digest,gethwversion,getrfversion,getprjversion,setprojmodel,sha256init,sha256final</span><br><span class="line">firehose -</span><br><span class="line">Writing to physical partition 4, sector 962718, sectors 1</span><br><span class="line">Traceback (most recent call last):</span><br><span class="line"> File <span class="string">"/Users/jiazhenjie/tools/edl/edl.py"</span>, line 358, <span class="keyword">in</span> <module></span><br><span class="line"> base.run()</span><br><span class="line"> File <span class="string">"/Users/jiazhenjie/tools/edl/edl.py"</span>, line 340, <span class="keyword">in</span> run</span><br><span class="line"> fh.handle_firehose(cmd, options)</span><br><span class="line"> File <span class="string">"/Users/jiazhenjie/tools/edl/edl/Library/firehose_client.py"</span>, line 651, <span class="keyword">in</span> handle_firehose</span><br><span class="line"> <span class="keyword">if</span> self.firehose.cmd_program(lun, startsector, filename):</span><br><span class="line"> File <span class="string">"/Users/jiazhenjie/tools/edl/edl/Library/firehose.py"</span>, line 438, <span class="keyword">in</span> cmd_program</span><br><span class="line"> data += self.modules.addprogram()</span><br><span class="line"> File <span class="string">"/Users/jiazhenjie/tools/edl/edl/Library/Modules/init.py"</span>, line 64, <span class="keyword">in</span> addprogram</span><br><span class="line"> <span class="built_in">return</span> self.ops.addprogram()</span><br><span class="line"> File <span class="string">"/Users/jiazhenjie/tools/edl/edl/Library/Modules/oneplus.py"</span>, line 233, <span class="keyword">in</span> addprogram</span><br><span class="line"> pk, token = self.ops.generatetoken(True)</span><br><span class="line">AttributeError: <span class="string">'NoneType'</span> object has no attribute <span class="string">'generatetoken'</span></span><br></pre></td></tr></table></figure><p>这个需要逆向刷机工具来分析了</p><h2 id="逆向MSM-Download"><a href="#逆向MSM-Download" class="headerlink" title="逆向MSM Download"></a>逆向MSM Download</h2><blockquote><p> 喜闻乐见的逆向环节</p></blockquote><h3 id="USB抓包"><a href="#USB抓包" class="headerlink" title="USB抓包"></a>USB抓包</h3><p>因为每次都会发token,所以想着抓个包,如果固定,那就万事大吉,结果发现不固定</p><figure class="highlight xml"><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="tag"><<span class="name">data</span>></span></span><br><span class="line"><span class="tag"><<span class="name">setprojmodel</span> <span class="attr">token</span>=<span class="string">"C5DB7CFB89D7A9DBB005388A52F8622FC20BDDD89F5CAD8ED42DEA046DE93F079F47021C7C2A8033300F437881B8FA799FE634A0B7876819DC612799A7B2822A4674B4312FC04FFB20CFE4F40CB487FBD8FDA78A9492E8B1AE0FEEBB0A88802497336B98A1DE35B0691AF563F2DED6837333AAAFE62AB576A73667AFA61E874FC0380223C9CFE3360ED9775014F0E921BE2C101DD979132412CB4E196A1CD05BEDFA19B13419F3DC722ECBA7CB54A9EE67930DE7EADCB0A31E272415A5DBF9948C2EB656D9925D35CE66B60ADFB7F66249319F2ABA9050D0C8019090214D595F59D23EEB2D6C65E8218B66134393A350EFAE4DC3030A6B4F7FC7AC576D07FFF2"</span> <span class="attr">pk</span>=<span class="string">"Yc9vlwu65U6PvhYO"</span> /></span></span><br><span class="line"><span class="tag"></<span class="name">data</span>></span></span><br></pre></td></tr></table></figure><p>这两个值并不是固定的,应该是固定算法+一些随机数算出来的</p><p>核心逻辑还是在计算这两个值,算是在刷写分区之前的验证工作,根据edl中的代码可知大概的逻辑,辅助逆向就方便多了</p><h3 id="token-amp-pk-逆向结果"><a href="#token-amp-pk-逆向结果" class="headerlink" title="token & pk 逆向结果"></a>token & pk 逆向结果</h3><blockquote><p> 先把结论放前面</p></blockquote><ul><li> prodkey固定 b2fad511325185e5</li><li> random_postfix 是随机字符串 // 这个可以写成固定值,反正是刷机工具生成的</li><li> 时间戳 随机</li><li> pk 随机</li></ul><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">head = c4b95538c57df231 </span><br><span class="line">tail = 5b0217457e49381b </span><br><span class="line">cf = <span class="number">0</span></span><br><span class="line">soc_sn = <span class="number">2360036966</span></span><br><span class="line">ModelVerifyPrjName = <span class="number">18821</span></span><br><span class="line">Version = guacamole_21_H<span class="number">.04_190416</span></span><br><span class="line">prodkey = b2fad511325185e5</span><br><span class="line">random_postfix = 随机的<span class="number">16</span>字节字符串</span><br><span class="line"></span><br><span class="line">ModelVerifyHashToken = sha256(prodkey + ModelVerifyPrjName + random_postfix)</span><br><span class="line">secret = sha256(head + ModelVerifyPrjName + cf + soc_sn + Version + 时间戳 + ModelVerifyHashToken + tail)</span><br><span class="line">items = [ModelVerifyPrjName, random_postfix, ModelVerifyHashToken, Version, cf, soc_sn, timestamp, secret]</span><br><span class="line"></span><br><span class="line">pk = 随机<span class="number">16</span>字节字符串 </span><br><span class="line">aeskey = <span class="string">b"\x10\x45\x63\x87\xE3\x7E\x23\x71"</span> + <span class="built_in">bytes</span>(pk, <span class="string">'utf-8'</span>) + <span class="string">b"\xA2\xD4\xA0\x74\x0f\xD3\x28\x96"</span></span><br><span class="line">aesiv = <span class="string">b"\x9D\x61\x4A\x1E\xAC\x81\xC9\xB2\xD3\x76\xD7\x49\x31\x03\x63\x79"</span></span><br><span class="line">pdata = <span class="string">"FEF0FFDA0CEF3E6C50E187E4A37D1B7DB860877A5F0ABFEC491DAC8DD5FD7F77D5D2859ADCDABED5B3018929CA10A00E786A675CD19184BB9BF2EF66A19AC234E4FD7EDFA8EB19E039B0FDD7BE0D3BC8DEA2453A6058D5370C923C9C4E632F3DEB1DA9F66F7BEA5B6D050B88C202BD5EEAA654DBF7AF410A14F5CB7DD481AEFAA6175685D565005D21CBBC2D62F860143FFE971F2845B2BD93A03ABDF6EE61F93E35740D8E2A09F89EB702D7E604914F0EDCE86F754FB994C1F82A20A094C8814EAD18FA6F24396A62A9C83D5412A53D740E662B7A9699ACA4352773B2F79374FF764EBC16143578481C0AD01135AE4BBA477C056320F690F4427E0635E91CEE"</span></span><br><span class="line"></span><br><span class="line">token = <span class="built_in">hex</span>(aes_cbc(items, key, iv))</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>为什么edl直接刷会失败呢?</p><ol><li> 没获取到prjid(18821),所以我在patch里直接硬编码了</li><li> prodkey不对</li></ol><figure class="highlight diff"><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"> def getprodkey(self, projid):</span><br><span class="line"><span class="deletion">- if projid in ["18825", "18801"]: # key_guacamoles, fajiita</span></span><br><span class="line"><span class="addition">+ if projid in ["18825", "18801", "18821"]: # key_guacamoles, fajiita, guacamole</span></span><br><span class="line"> prodkey = "b2fad511325185e5"</span><br><span class="line"> else: # key_op7t/op8/N10</span><br><span class="line"> prodkey = "7016147d58e8c038"</span><br><span class="line"><span class="meta">@@ -164,7 +165,6 @@</span> class oneplus(metaclass=LogBase):</span><br><span class="line"> rand = int(random.randint(0, 0x100))</span><br><span class="line"> nr = (rand & 0xFF) % 0x3E</span><br><span class="line"> pk += chr(val[nr])</span><br></pre></td></tr></table></figure><p>其他地方这个工具都是对的,逆向过程的笔记没整理,也比较简单,没壳没混淆的,找到关键位置慢慢看就行了。</p><h2 id="尝试修改分区实现unlock"><a href="#尝试修改分区实现unlock" class="headerlink" title="尝试修改分区实现unlock"></a>尝试修改分区实现unlock</h2><blockquote><p> 想模拟一下把ufs吹下来修改后焊回去的操作</p></blockquote><p>修改了edl之后发现是可以正常写分区的</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221102222117218.png" alt="image-20221102222117218"></p><p>修改devinfo之后刷回去,发现还是locked,看来只改这里是不行的, GG~</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221102222156841.png" alt="image-20221102222156841"></p><h2 id="VerifiedBoot-Protocol-分析"><a href="#VerifiedBoot-Protocol-分析" class="headerlink" title="VerifiedBoot Protocol 分析"></a>VerifiedBoot Protocol 分析</h2><blockquote><p> abl分析的时候提到了这个protocol,所以想搞清楚为什么失败就要看看这里</p></blockquote><p>根据<code>boot_images/QcomPkg/Drivers/VerifiedBootDxe/VerifiedBootDxe.inf</code></p><p>可知对应的实现在同目录的 <code>VerifiedBootDxe.c</code> 中</p><p>这里注册了这个protocol</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></pre></td><td class="code"><pre><span class="line"><span class="function">EFI_STATUS</span></span><br><span class="line"><span class="function">EFIAPI</span></span><br><span class="line"><span class="function"><span class="title">VerifiedBootDxeEntryPoint</span><span class="params">(IN EFI_HANDLE ImageHandle,</span></span></span><br><span class="line"><span class="params"><span class="function"> IN EFI_SYSTEM_TABLE *SystemTable)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> EFI_STATUS Status;</span><br><span class="line"> EFI_HANDLE Handle = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"> Status = gBS->InstallMultipleProtocolInterfaces(</span><br><span class="line"> &Handle, &gEfiQcomVerifiedBootProtocolGuid,</span><br><span class="line"> (VOID **)&QCOMVerifiedBootProtocol, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> Status;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>要看的方法是 <code>VBRwDeviceState</code> 对应的是 <code>QCOM_VB_RWDeviceState</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* We use devinfo partition when the device is not secure */</span></span><br><span class="line"> AsciiStrnCpy((CHAR8 *)img_name, <span class="string">"devinfo"</span>, AsciiStrLen(<span class="string">"devinfo"</span>));</span><br><span class="line"> <span class="keyword">if</span> (convert_char8_to_char16(img_name, img_label, AsciiStrLen(<span class="string">"devinfo"</span>)) != EFI_SUCCESS) {</span><br><span class="line"> status = RETURN_INVALID_PARAMETER;</span><br><span class="line"> <span class="keyword">goto</span> <span class="built_in">exit</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>显然,只有没烧efuse的时候才会用devinfo作为存放是否unlock的标志,这一点也符合预期,至此这次探索基本上就结束了。</p><h2 id="结束"><a href="#结束" class="headerlink" title="结束"></a>结束</h2><p>我也把firehose和对edl的patch放到了<a href="https://github.com/o0xmuhe/play_with_oneplus7pro">github</a>,过程也确实好玩:) 不过还是有不少没研究到的地方,比如他的verifyboot实现是否安全啥的 -。-</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://www.droidwin.com/how-to-extract-oneplus-ops-firmware/">https://www.droidwin.com/how-to-extract-oneplus-ops-firmware/</a></p><p><a href="https://zhuanlan.zhihu.com/p/427390226">https://zhuanlan.zhihu.com/p/427390226</a></p><p><a href="https://github.com/theopolis/uefi-firmware-parser">https://github.com/theopolis/uefi-firmware-parser</a></p><p><a href="https://blog.omitol.com/2017/09/30/Bypass-QCOM-Secure-Boot">https://blog.omitol.com/2017/09/30/Bypass-QCOM-Secure-Boot</a></p><p><a href="https://tjtech.me/analyze-oem-unlocking-under-android.html">https://tjtech.me/analyze-oem-unlocking-under-android.html</a></p><p><a href="https://www.oneplusbbs.com/forum.php?mod=viewthread&tid=4730052">https://www.oneplusbbs.com/forum.php?mod=viewthread&tid=4730052</a></p>]]></content>
<summary type="html"><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p> 严格意义上来说本文应该叫做: &lt;&lt;我本来只是想救个砖,但是却逆向了刷机工具尝试搞清楚<code>android unlock</code>的原理&gt;&gt; :D </p></summary>
<category term="学习记录" scheme="https://o0xmuhe.github.io/categories/%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/"/>
<category term="Android" scheme="https://o0xmuhe.github.io/tags/Android/"/>
<category term="Unlock" scheme="https://o0xmuhe.github.io/tags/Unlock/"/>
<category term="Qualcomm" scheme="https://o0xmuhe.github.io/tags/Qualcomm/"/>
<category term="EDL" scheme="https://o0xmuhe.github.io/tags/EDL/"/>
</entry>
<entry>
<title>optee学习(2) CA&TA调用流程分析</title>
<link href="https://o0xmuhe.github.io/2022/10/26/optee%E5%AD%A6%E4%B9%A0-2-CA-TA/"/>
<id>https://o0xmuhe.github.io/2022/10/26/optee%E5%AD%A6%E4%B9%A0-2-CA-TA/</id>
<published>2022-10-26T15:33:52.000Z</published>
<updated>2022-11-14T11:42:37.327Z</updated>
<content type="html"><![CDATA[<h2 id="环境"><a href="#环境" class="headerlink" title="环境"></a>环境</h2><ul><li> ubuntu22.04 </li><li> ADS + optee-fvp</li></ul><h2 id="调用流程梳理"><a href="#调用流程梳理" class="headerlink" title="调用流程梳理"></a>调用流程梳理</h2><p> 这里直接从optee-examples中最简单的hello world入手来看的,从宏观上来看整个调用流程是 :</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">CA --> optee client --> tee driver --> ATF --> TEE --> TA</span><br></pre></td></tr></table></figure><span id="more"></span><p>根据个人的理解画了个省流版本的图,省略了部分调用</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/optee_ca_ta.png" alt="optee_ca_ta"></p><h3 id="CA-amp-TA-的工作流程"><a href="#CA-amp-TA-的工作流程" class="headerlink" title="CA & TA 的工作流程"></a>CA & TA 的工作流程</h3><ul><li> CA</li></ul><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></pre></td><td class="code"><pre><span class="line">//1. 初始化context用于和TEE交互</span><br><span class="line">res = TEEC_InitializeContext(NULL, &ctx);</span><br><span class="line"></span><br><span class="line">//2. 打开“会话”,此时TEE侧会验证并且加载对应的TA</span><br><span class="line">res = TEEC_OpenSession(&ctx, &sess, &uuid,</span><br><span class="line"> TEEC_LOGIN_PUBLIC, NULL, NULL, &err_origin);</span><br><span class="line"></span><br><span class="line">//3. 交互,通过invoke command来触发,调用到TA里具体的逻辑</span><br><span class="line">res = TEEC_InvokeCommand(&sess, TA_HELLO_WORLD_CMD_INC_VALUE, &op,</span><br><span class="line"> &err_origin);</span><br><span class="line"></span><br><span class="line">//4. 使用完毕,关闭“会话”</span><br><span class="line">TEEC_CloseSession(&sess);</span><br><span class="line"></span><br><span class="line">// 5. 释放context对象</span><br><span class="line">TEEC_FinalizeContext(&ctx);</span><br></pre></td></tr></table></figure><ul><li> TA </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><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1.</span> 执行的入口,会话的另一端</span><br><span class="line">TA_CreateEntryPoint <span class="comment">// TA加载的时候执行</span></span><br><span class="line">TA_OpenSessionEntryPoint</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="number">2.</span> 交互,业务代码</span><br><span class="line"><span class="function">TEE_Result <span class="title">TA_InvokeCommandEntryPoint</span><span class="params">(<span class="keyword">void</span> __maybe_unused *sess_ctx,</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="keyword">uint32_t</span> cmd_id,</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="keyword">uint32_t</span> param_types, TEE_Param params[<span class="number">4</span>])</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> (<span class="keyword">void</span>)&sess_ctx; <span class="comment">/* Unused parameter */</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">switch</span> (cmd_id) {</span><br><span class="line"> <span class="keyword">case</span> TA_HELLO_WORLD_CMD_INC_VALUE:</span><br><span class="line"> <span class="keyword">return</span> inc_value(param_types, params);</span><br><span class="line"> <span class="keyword">case</span> TA_HELLO_WORLD_CMD_DEC_VALUE:</span><br><span class="line"> <span class="keyword">return</span> dec_value(param_types, params);</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">return</span> TEE_ERROR_BAD_PARAMETERS;</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="number">3.</span> 交互完毕,关闭会话</span><br><span class="line">TA_CloseSessionEntryPoint</span><br><span class="line">TA_DestroyEntryPoint</span><br></pre></td></tr></table></figure><ul><li> CA 和 TA的对应关系</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></pre></td><td class="code"><pre><span class="line">TEEC_OpenSession -> TA_CreateEntryPoint</span><br><span class="line"> TA_OpenSessionEntryPoint</span><br><span class="line"></span><br><span class="line">TEEC_InvokeCommand -> TA_InvokeCommandEntryPoint</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">TEEC_CloseSession -> TA_CloseSessionEntryPoint</span><br><span class="line"> TA_DestroyEntryPoint</span><br><span class="line"> </span><br></pre></td></tr></table></figure><h2 id="源码阅读"><a href="#源码阅读" class="headerlink" title="源码阅读"></a>源码阅读</h2><h3 id="TEEC-InitializeContext"><a href="#TEEC-InitializeContext" class="headerlink" title="TEEC_InitializeContext"></a>TEEC_InitializeContext</h3><p>TEEC_InitializeContext → 打开tee driver,要用于通信了 ,主要是一些初始化的工作</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">TEEC_InitializeContext</span></span><br><span class="line"><span class="function"> teec_open_dev</span></span><br><span class="line"><span class="function"> <span class="title">ioctl</span><span class="params">(fd, TEE_IOC_VERSION, &vers)</span></span></span><br></pre></td></tr></table></figure><p>注意此时的CMD是 <code>TEE_IOC_VERSION</code>,对应执行的是 <code>tee_ioctl_version</code></p><h3 id="TEEC-OpenSession"><a href="#TEEC-OpenSession" class="headerlink" title="TEEC_OpenSession"></a>TEEC_OpenSession</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// context</span></span><br><span class="line"><span class="comment">// tee session</span></span><br><span class="line"><span class="comment">// TA的uuid,唯一</span></span><br><span class="line"><span class="comment">// connection method</span></span><br><span class="line"><span class="comment">// connection data</span></span><br><span class="line"><span class="comment">// operation </span></span><br><span class="line"><span class="comment">// ret </span></span><br><span class="line">TEEC_OpenSession(&ctx, &sess, &uuid, TEEC_LOGIN_PUBLIC, <span class="literal">NULL</span>, <span class="literal">NULL</span>, &err_origin);</span><br><span class="line">....</span><br><span class="line"></span><br><span class="line">rc = ioctl(ctx->fd, TEE_IOC_OPEN_SESSION, &buf_data);</span><br></pre></td></tr></table></figure><p>此时CMD是 <code>TEE_IOC_OPEN_SESSION</code>,到tee driver中查看对应的处理逻辑 : </p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221112203936455.png" alt="image-20221112203936455"></p><p>往后会调用到对应的handler: </p><figure class="highlight c"><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">rc = ctx->teedev->desc->ops->open_session(ctx, &arg, params);</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>在进TEE之前,传递的参数需要做转换,反过来也是;从REE往TEE走,其实是一个入口 do_call_with_arg,这些<code>operations</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * struct optee_ops - OP-TEE driver internal operations</span></span><br><span class="line"><span class="comment"> * @do_call_with_arg: enters OP-TEE in secure world</span></span><br><span class="line"><span class="comment"> * @to_msg_param: converts from struct tee_param to OPTEE_MSG parameters</span></span><br><span class="line"><span class="comment"> * @from_msg_param: converts from OPTEE_MSG parameters to struct tee_param</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * These OPs are only supposed to be used internally in the OP-TEE driver</span></span><br><span class="line"><span class="comment"> * as a way of abstracting the different methogs of entering OP-TEE in</span></span><br><span class="line"><span class="comment"> * secure world.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">optee_ops</span> {</span></span><br><span class="line"> <span class="keyword">int</span> (*do_call_with_arg)(struct tee_context *ctx,</span><br><span class="line"> struct tee_shm *shm_arg, u_int offs);</span><br><span class="line"> <span class="keyword">int</span> (*to_msg_param)(struct optee *optee,</span><br><span class="line"> struct optee_msg_param *msg_params,</span><br><span class="line"> <span class="keyword">size_t</span> num_params, <span class="keyword">const</span> struct tee_param *params);</span><br><span class="line"> <span class="keyword">int</span> (*from_msg_param)(struct optee *optee, struct tee_param *params,</span><br><span class="line"> <span class="keyword">size_t</span> num_params,</span><br><span class="line"> <span class="keyword">const</span> struct optee_msg_param *msg_params);</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>直接在目录中搜<code>open_session</code> </p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221112204128044.png" alt="image-20221112204128044"></p><p>发现有两个实现,这里的话<code>ffa_abi.c</code>中的应该是FF-A标准对应的那个实现,这里直接看smc的那个就行, 即<code>linux/drivers/tee/optee/smc_abi.c </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></pre></td><td class="code"><pre><span class="line"><span class="number">910</span> <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line"><span class="number">911</span> <span class="class"><span class="keyword">struct</span> <span class="title">arm_smccc_res</span> <span class="title">res</span>;</span></span><br><span class="line"><span class="number">912</span> </span><br><span class="line"><span class="number">913</span> trace_optee_invoke_fn_begin(&param);</span><br><span class="line"><span class="number">914</span> optee->smc.invoke_fn(param.a0, param.a1, param.a2, param.a3,</span><br><span class="line"><span class="number">915</span> param.a4, param.a5, param.a6, param.a7,</span><br><span class="line"><span class="number">916</span> &res);</span><br><span class="line"><span class="number">917</span> trace_optee_invoke_fn_end(&param, &res);</span><br><span class="line"><span class="number">918</span> </span><br><span class="line"><span class="number">919</span> <span class="keyword">if</span> (res.a0 == OPTEE_SMC_RETURN_ETHREAD_LIMIT) {</span><br><span class="line"><span class="number">920</span> <span class="comment">/*</span></span><br><span class="line"><span class="comment">921 * Out of threads in secure world, wait for a thread</span></span><br><span class="line"><span class="comment">922 * become available.</span></span><br><span class="line"><span class="comment">923 */</span></span><br><span class="line"><span class="number">924</span> optee_cq_wait_for_completion(&optee->call_queue, &w);</span><br><span class="line"><span class="number">925</span> } <span class="keyword">else</span> <span class="keyword">if</span> (OPTEE_SMC_RETURN_IS_RPC(res.a0)) {</span><br><span class="line"><span class="number">926</span> cond_resched();</span><br><span class="line"><span class="number">927</span> param.a0 = res.a0;</span><br><span class="line"><span class="number">928</span> param.a1 = res.a1;</span><br><span class="line"><span class="number">929</span> param.a2 = res.a2;</span><br><span class="line"><span class="number">930</span> param.a3 = res.a3;</span><br><span class="line"><span class="number">931</span> optee_handle_rpc(ctx, rpc_arg, &param, &call_ctx);</span><br><span class="line"><span class="number">932</span> } <span class="keyword">else</span> {</span><br><span class="line"><span class="number">933</span> rc = res.a0;</span><br><span class="line"><span class="number">934</span> <span class="keyword">break</span>;</span><br><span class="line"><span class="number">935</span> }</span><br><span class="line"><span class="number">936</span> }</span><br></pre></td></tr></table></figure><p>中间这个 <code>smc.invoke_fn</code>就是通过smc进入到ATF,然后ATF会转发到TEE处理</p><p>对于ATF来说,这是一个通过 <code>SMC #0</code> 过来的中断,这是core内部发生的,且异常等级发生了变化,所以应该是到了ATF的第三组向量表的sync中断处理程序处</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221112204311537.png" alt="image-20221112204311537"></p><p>这里细节就不深入看了,主要是为了梳理工作流程,ATF里会调用到系统启动的时候注册的optee的tspd来处理,(opteed_smc_handler 函数)</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221112204331216.png" alt="image-20221112204331216"></p><p>这个handler里会保存 non-secure的上下文,恢复secure的上下文,然后直接eret到TEE侧。</p><p>进入optee之后来到:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221112204354659.png" alt="image-20221112204354659"></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="number">53</span> <span class="function"><span class="keyword">uint32_t</span> <span class="title">thread_handle_std_smc</span><span class="params">(<span class="keyword">uint32_t</span> a0, <span class="keyword">uint32_t</span> a1, <span class="keyword">uint32_t</span> a2,</span></span></span><br><span class="line"><span class="params"><span class="function"><span class="number">54</span> <span class="keyword">uint32_t</span> a3, <span class="keyword">uint32_t</span> a4, <span class="keyword">uint32_t</span> a5,</span></span></span><br><span class="line"><span class="params"><span class="function"><span class="number">55</span> <span class="keyword">uint32_t</span> a6 __unused, <span class="keyword">uint32_t</span> a7 __maybe_unused)</span></span></span><br><span class="line"><span class="function">56 </span>{</span><br><span class="line"> ....</span><br><span class="line"></span><br><span class="line"><span class="number">69</span> <span class="keyword">if</span> (a0 == OPTEE_SMC_CALL_RETURN_FROM_RPC) {</span><br><span class="line"><span class="number">70</span> thread_resume_from_rpc(a3, a1, a2, a4, a5);</span><br><span class="line"><span class="number">71</span> rv = OPTEE_SMC_RETURN_ERESUME;</span><br><span class="line"><span class="number">72</span> } <span class="keyword">else</span> {</span><br><span class="line"><span class="number">73</span> thread_alloc_and_run(a0, a1, a2, a3, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"><span class="number">74</span> rv = OPTEE_SMC_RETURN_ETHREAD_LIMIT;</span><br><span class="line"><span class="number">75</span> }</span><br><span class="line"></span><br><span class="line"> ...</span><br></pre></td></tr></table></figure><p>第一次走到 thread_alloc_and_run,传入参数是 <code>thread_std_smc_entry</code>, 所以会执行到 <code>thread_std_smc_entry</code></p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221112204452280.png" alt="image-20221112204452280"></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></pre></td><td class="code"><pre><span class="line"><span class="function">__thread_std_smc_entry</span></span><br><span class="line"><span class="function"> <span class="title">std_smc_entry</span><span class="params">(a0, a1, a2, a3)</span></span>;</span><br><span class="line"> std_entry_with_parg(...)</span><br><span class="line"> call_entry_std</span><br><span class="line"> tee_entry_std</span><br><span class="line"> __tee_entry_std</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><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">538</span> TEE_Result __tee_entry_std(struct optee_msg_arg *arg, <span class="keyword">uint32_t</span> num_params)</span><br><span class="line"><span class="number">539</span> {</span><br><span class="line"><span class="number">540</span> TEE_Result res = TEE_SUCCESS;</span><br><span class="line"><span class="number">541</span> </span><br><span class="line"><span class="number">542</span> <span class="comment">/* Enable foreign interrupts for STD calls */</span></span><br><span class="line"><span class="number">543</span> thread_set_foreign_intr(<span class="literal">true</span>);</span><br><span class="line"><span class="number">544</span> <span class="keyword">switch</span> (arg->cmd) {</span><br><span class="line"><span class="number">545</span> <span class="keyword">case</span> OPTEE_MSG_CMD_OPEN_SESSION:</span><br><span class="line"><span class="number">546</span> entry_open_session(arg, num_params);</span><br><span class="line"><span class="number">547</span> <span class="keyword">break</span>;</span><br><span class="line"><span class="number">548</span> <span class="keyword">case</span> OPTEE_MSG_CMD_CLOSE_SESSION:</span><br><span class="line"><span class="number">549</span> entry_close_session(arg, num_params);</span><br><span class="line"><span class="number">550</span> <span class="keyword">break</span>;</span><br><span class="line"><span class="number">551</span> <span class="keyword">case</span> OPTEE_MSG_CMD_INVOKE_COMMAND:</span><br><span class="line"><span class="number">552</span> entry_invoke_command(arg, num_params);</span><br><span class="line"><span class="number">553</span> <span class="keyword">break</span>;</span><br><span class="line"><span class="number">554</span> <span class="keyword">case</span> OPTEE_MSG_CMD_CANCEL:</span><br><span class="line"><span class="number">555</span> entry_cancel(arg, num_params);</span><br><span class="line"><span class="number">556</span> <span class="keyword">break</span>;</span><br><span class="line"><span class="number">557</span> <span class="meta">#<span class="meta-keyword">ifndef</span> CFG_CORE_FFA</span></span><br><span class="line"><span class="number">558</span> <span class="meta">#<span class="meta-keyword">ifdef</span> CFG_CORE_DYN_SHM</span></span><br><span class="line"><span class="number">559</span> <span class="keyword">case</span> OPTEE_MSG_CMD_REGISTER_SHM:</span><br><span class="line"><span class="number">560</span> register_shm(arg, num_params);</span><br><span class="line"><span class="number">561</span> <span class="keyword">break</span>;</span><br><span class="line"><span class="number">562</span> <span class="keyword">case</span> OPTEE_MSG_CMD_UNREGISTER_SHM:</span><br><span class="line"><span class="number">563</span> unregister_shm(arg, num_params);</span><br><span class="line"><span class="number">564</span> <span class="keyword">break</span>;</span><br><span class="line"><span class="number">565</span> <span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="number">566</span> <span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="number">567</span> </span><br><span class="line"><span class="number">568</span> <span class="keyword">case</span> OPTEE_MSG_CMD_DO_BOTTOM_HALF:</span><br><span class="line"><span class="number">569</span> <span class="keyword">if</span> (IS_ENABLED(CFG_CORE_ASYNC_NOTIF))</span><br><span class="line"><span class="number">570</span> notif_deliver_event(NOTIF_EVENT_DO_BOTTOM_HALF);</span><br><span class="line"><span class="number">571</span> <span class="keyword">else</span></span><br><span class="line"><span class="number">572</span> <span class="keyword">goto</span> err;</span><br><span class="line"><span class="number">573</span> <span class="keyword">break</span>;</span><br><span class="line"><span class="number">574</span> <span class="keyword">case</span> OPTEE_MSG_CMD_STOP_ASYNC_NOTIF:</span><br><span class="line"><span class="number">575</span> <span class="keyword">if</span> (IS_ENABLED(CFG_CORE_ASYNC_NOTIF))</span><br><span class="line"><span class="number">576</span> notif_deliver_event(NOTIF_EVENT_STOPPED);</span><br><span class="line"><span class="number">577</span> <span class="keyword">else</span></span><br><span class="line"><span class="number">578</span> <span class="keyword">goto</span> err;</span><br><span class="line"><span class="number">579</span> <span class="keyword">break</span>;</span><br><span class="line"><span class="number">580</span> </span><br><span class="line"><span class="number">581</span> <span class="keyword">default</span>:</span><br><span class="line"><span class="number">582</span> err:</span><br><span class="line"><span class="number">583</span> EMSG(<span class="string">"Unknown cmd 0x%x"</span>, arg->cmd);</span><br><span class="line"><span class="number">584</span> res = TEE_ERROR_NOT_IMPLEMENTED;</span><br><span class="line"><span class="number">585</span> }</span><br><span class="line"><span class="number">586</span> </span><br><span class="line"><span class="number">587</span> <span class="keyword">return</span> res;</span><br><span class="line"><span class="number">588</span> }</span><br></pre></td></tr></table></figure><p>这次的cmd是 open session所以走 <code>entry_open_session</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></pre></td><td class="code"><pre><span class="line"><span class="number">373</span> res = tee_ta_open_session(&err_orig, &s, &tee_open_sessions, &uuid,</span><br><span class="line"><span class="number">374</span> &clnt_id, TEE_TIMEOUT_INFINITE, &param);</span><br><span class="line"></span><br><span class="line"><span class="comment">// uuid,需要根据uuid来加载TA了</span></span><br></pre></td></tr></table></figure><p>然后去加载对应的TA,在 tee_ta_open_session // tee_ta_manager.c</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="number">715</span> res = tee_ta_init_session(err, open_sessions, uuid, &s);</span><br></pre></td></tr></table></figure><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221112204603153.png" alt="image-20221112204603153"></p><p>加载完毕之后,如果成功加载了,那就调用 <code>ts_ctx->ops->enter_open_session(&s->ts_sess);</code></p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221112204739124.png" alt="image-20221112204739124"></p><p>根据注册信息,应该是 <code>user_ta_enter_open_session</code></p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221112204803734.png" alt="image-20221112204803734"></p><p>调用到 user_ta_enter 函数,此时还是在optee里的,需要跳到TA去执行</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"><span class="number">166</span> res = thread_enter_user_mode(func, kaddr_to_uref(session),</span><br><span class="line"><span class="number">167</span> (<span class="keyword">vaddr_t</span>)usr_params, cmd, usr_stack,</span><br><span class="line"><span class="number">168</span> utc->uctx.entry_func, utc->uctx.is_32bit,</span><br><span class="line"><span class="number">169</span> &utc->ta_ctx.panicked,</span><br><span class="line"><span class="number">170</span> &utc->ta_ctx.panic_code);</span><br></pre></td></tr></table></figure><p>S-EL1 → S-EL0,应该是eret过去的</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">__thread_enter_user_mode(regs, exit_status0, exit_status1);</span><br><span class="line"> b eret_to_el0</span><br><span class="line"> eret </span><br></pre></td></tr></table></figure><p>跳转前设置好了上下文,所以eret后就回到了TA中执行,这就到了TA中的 <code>TA_OpenSessionEntryPoint</code></p><h3 id="TEEC-InvokeCommand"><a href="#TEEC-InvokeCommand" class="headerlink" title="TEEC_InvokeCommand"></a>TEEC_InvokeCommand</h3><p>逻辑基本和上面OpenSession差不多,差别就在于传递的 <code>InvokeCommand</code></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></pre></td><td class="code"><pre><span class="line"><span class="function">user_ta_enter_invoke_cmd</span></span><br><span class="line"><span class="function"> <span class="title">user_ta_enter</span><span class="params">(s, UTEE_ENTRY_FUNC_INVOKE_COMMAND, cmd)</span></span>;</span><br></pre></td></tr></table></figure><p>然后调用到TA的 <strong><code>TEEC_InvokeCommand</code> 函数</strong></p><h3 id="TEEC-CloseSession"><a href="#TEEC-CloseSession" class="headerlink" title="TEEC_CloseSession"></a>TEEC_CloseSession</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">TEEC_CloseSession</span><span class="params">(TEEC_Session *session)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">tee_ioctl_close_session_arg</span> <span class="title">arg</span>;</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">memset</span>(&arg, <span class="number">0</span>, <span class="keyword">sizeof</span>(arg));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!session)</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line"> arg.session = session->session_id;</span><br><span class="line"> <span class="keyword">if</span> (ioctl(session->ctx->fd, TEE_IOC_CLOSE_SESSION, &arg))</span><br><span class="line"> EMSG(<span class="string">"Failed to close session 0x%x"</span>, session->session_id);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>也是类似的情况,调用到内核里tee_ioctl_close_session ,区别只是cmd不同,最后会一路到TA侧的 TA_CloseSessionEntryPoint</p><h3 id="TEEC-FinalizeContext"><a href="#TEEC-FinalizeContext" class="headerlink" title="TEEC_FinalizeContext"></a>TEEC_FinalizeContext</h3><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">TEEC_FinalizeContext</span><span class="params">(TEEC_Context *ctx)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">if</span> (ctx)</span><br><span class="line"> close(ctx->fd);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="调试"><a href="#调试" class="headerlink" title="调试"></a>调试</h2><p>根据上面的流程梳理,只要在optee 往TA里跳的时候下个断,就能去分析TA了,然后再加载TA的符号就能快乐地debug了,没有源码那就纯黑盒调试TA了</p><p>结合<a href="https://github.com/ForgeRock/optee-build/blob/master/docs/debug.md#15-debugging-ta">optee的文档</a> 里的描述,会用到TA的 .text段 LMA信息</p><figure class="highlight c"><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">$ objdump -h <span class="number">8</span>aaaf200<span class="number">-2450</span><span class="number">-11e4</span>-abe2<span class="number">-0002</span>a5d5c51b.elf | grep <span class="string">".text"</span></span><br><span class="line"> <span class="number">1</span> .text <span class="number">00012e5</span>c <span class="number">00000020</span> <span class="number">00000020</span> <span class="number">00001020</span> <span class="number">2</span>**<span class="number">2</span></span><br></pre></td></tr></table></figure><p>启动ADS,然后在加载tee的时候断住,加载tee的符号,参考<a href="https://o0xmuhe.github.io/2022/08/24/optee%E5%AD%A6%E4%B9%A0/#bl32">我上一篇博客</a>就行了。</p><blockquote><p> 如果想调试全部的过程,按照文章把 Linux kernel、 bl31 runtime 的符号也加载进来就行了</p></blockquote><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">b user_ta_enter_open_session</span><br></pre></td></tr></table></figure><p>然后执行CA,可以观察到已经断下来了</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221112205051157.png" alt="image-20221112205051157"></p><p>其实这个时候TEE侧log已经看到了TA被加载到了哪里了,直接下断也可以的</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">b *EL0S:0x40060020</span><br></pre></td></tr></table></figure><p>但是没断下来且报错了,很奇怪的是eret之后 还是显示SEL1,我查看了currentel寄存器之后发现确实是在EL0的</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221112205212578.png" alt="image-20221112205212578"></p><p>问了下组里的大佬,这个反汇编窗口显示的<code>ELxS/N</code> 应该是这块内存的属性,而不是当前执行状态 (之前直接靠这个tag来做判断,看来是错的离谱了)</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221112205228983.png" alt="image-20221112205228983"></p><blockquote><p> 个人猜测 因为TA加载是optee做的,所以可能optee分配出来的内存就是EL1S,所以跑到TA的时候,反汇编窗口地址tag会显示EL1S</p></blockquote><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">add-symbol-file /home/muhe/Study/optee-fvp/out-br/build/optee_examples_ext-1.0/hello_world/ta/out/8aaaf200-2450-11e4-abe2-0002a5d5c51b.elf 0x40060020</span><br></pre></td></tr></table></figure><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20221112205413157.png" alt="image-20221112205413157"></p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><blockquote><p><a href="https://blog.csdn.net/weixin_42135087/article/details/119384252">https://blog.csdn.net/weixin_42135087/article/details/119384252</a></p><p><a href="https://www.timesys.com/security/trusted-software-development-op-tee/">https://www.timesys.com/security/trusted-software-development-op-tee/</a></p><p><a href="https://optee.readthedocs.io/en/latest/building/gits/optee_examples/optee_examples.html">https://optee.readthedocs.io/en/latest/building/gits/optee_examples/optee_examples.html</a></p></blockquote>]]></content>
<summary type="html"><h2 id="环境"><a href="#环境" class="headerlink" title="环境"></a>环境</h2><ul>
<li> ubuntu22.04 </li>
<li> ADS + optee-fvp</li>
</ul>
<h2 id="调用流程梳理"><a href="#调用流程梳理" class="headerlink" title="调用流程梳理"></a>调用流程梳理</h2><p> 这里直接从optee-examples中最简单的hello world入手来看的,从宏观上来看整个调用流程是 :</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">CA --&gt; optee client --&gt; tee driver --&gt; ATF --&gt; TEE --&gt; TA</span><br></pre></td></tr></table></figure></summary>
<category term="optee" scheme="https://o0xmuhe.github.io/categories/optee/"/>
<category term="debug" scheme="https://o0xmuhe.github.io/tags/debug/"/>
<category term="optee" scheme="https://o0xmuhe.github.io/tags/optee/"/>
</entry>
<entry>
<title>Paper read <<The Convergence of Source Code and Binary Vulnerability Discovery – A Case Study>></title>
<link href="https://o0xmuhe.github.io/2022/09/12/Paper-read-The-Convergence-of-Source-Code-and-Binary-Vulnerability-Discovery-%E2%80%93-A-Case-Study/"/>
<id>https://o0xmuhe.github.io/2022/09/12/Paper-read-The-Convergence-of-Source-Code-and-Binary-Vulnerability-Discovery-%E2%80%93-A-Case-Study/</id>
<published>2022-09-12T05:33:26.000Z</published>
<updated>2022-11-13T07:59:48.027Z</updated>
<content type="html"><![CDATA[<h2 id="Background"><a href="#Background" class="headerlink" title="Background"></a><code>Background</code></h2><p> 最近阅读了一篇论文<code><<The Convergence of Source Code and Binary Vulnerability Discovery – A Case Study>></code>,很巧合的是论文的研究中,关于将SAST工具应用于二进制文件(通过decompiler),即获取伪代码之后,在伪代码上跑SAST工具来找漏洞这个模式我和<code>@C0ss4ck</code>一起做过,在我们收到一些成效之后发现也有人做了<a href="https://security.humanativaspa.it/automating-binary-vulnerability-discovery-with-ghidra-and-semgrep/">类似的工作</a>,不过他好像没有特别深入 :D </p><span id="more"></span><p> 我们这做主要是因为一些不可说的原因,最开始是<code>@C0ss4ck</code>搞的用IDAPython搞的工具,但是由于做适配比较麻烦不够灵活;后来我提出了<code>decompiler+weggli</code>的做法的时候,我们都不是那么的看好,但是搞了一些demo发现确实可行,对于一些简单的漏洞模型是可以召回的,主要的瓶颈就在decompile code的质量和规则的编写了,同时由于weggli本身不支持数据流,并且主要是过程内的漏洞模式匹配(AST regexp),所以后面就又面临瓶颈的问题了;在我做调研的时候,发现了这篇新鲜的论文,在读完之后感触良多,对<code>decompiler+SAST</code>的做法也有了更多的理解。</p><h2 id="Read-this-PAPER"><a href="#Read-this-PAPER" class="headerlink" title="Read this PAPER"></a>Read this <code>PAPER</code></h2><p> 这篇论文讨论了源码/伪代码+SAST工具在漏洞挖掘上的效果,以及对于<code>伪代码+SAST</code>这种模式的局限性的探究,对其中的误报&漏报根本原因的分析。 </p><h3 id="关于论文中的实验设计"><a href="#关于论文中的实验设计" class="headerlink" title="关于论文中的实验设计"></a>关于论文中的实验设计</h3><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220912140406820.png" alt="image-20220912140406820"></p><ol><li>源码直接使用 SAST 工具</li><li>多种decompiler反编译之后获取伪代码,丢给SAST工具</li><li>伪代码修正之后(达到可编译的程度,有些复杂目标要裁剪),给SAST工具用</li></ol><h3 id="工具-amp-目标选择"><a href="#工具-amp-目标选择" class="headerlink" title="工具 & 目标选择"></a>工具 & 目标选择</h3><blockquote><p> 尝试召回 real world vulns</p></blockquote><p>基本上都是优秀的工具,其中两款商业工具并没有写具体是啥,但是这个 <code>Comm_1</code>看起来好像Coverity :) 不管怎么样,其中的 <code>codeql</code>和<code>joern</code>我很感兴趣,毕竟可以自定义规则,这对我来说无疑是更好的,可以召回更多问题 & 适用于更多的场景。</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220912141551810.png" alt="image-20220912141551810"></p><p>对于漏洞的选择,该论文也选的比较广泛,各种类型都有,复杂度也够,可以更好的“测量”这些工具 :)</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220912142030762.png" alt="image-20220912142030762"></p><h3 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h3><ol><li><p> 反编译代码并不是开箱即用的</p></li><li><p> 对于二进制文件,<code>伪代码+SAST</code>的模式可行,但是有限</p></li><li><p><code>SAST</code>工具设计上是给源码用的,这是<code>by design</code>的;二进制文件丢失了关键信息(尤其是编译器优化的影响),不适合给<code>SAST</code>工具做分析,这也是为什么论文中说不用LLVM Lifter的原因</p><blockquote><p> decompilers are still designed to generate code that is easy to understand for humans, and SAST tools are still designed to parse “well-written” code that is not generated by a machine.</p></blockquote></li><li><p> <strong>编译器优化很有意思,有些漏洞因为优化inline,所以从过程间–>过程内,decompile之后的代码反而找到了漏洞 :)</strong></p></li></ol><blockquote><p>优化的话, 两个思路,相当于朝着同一个方向前进的路:</p></blockquote><ol><li>提高反编译代码的质量</li><li>优化<code>SAST</code>工具,让其适配反编译的代码</li></ol><h3 id="漏报-amp-误报-Root-cause"><a href="#漏报-amp-误报-Root-cause" class="headerlink" title="漏报 & 误报 Root cause"></a>漏报 & 误报 Root cause</h3><blockquote><p> 个人认为比较核心的地方了</p></blockquote><h4 id="P1-Inability-to-Recover-the-Size-of-Stack-Buffers"><a href="#P1-Inability-to-Recover-the-Size-of-Stack-Buffers" class="headerlink" title="P1 - Inability to Recover the Size of Stack Buffers"></a>P1 - Inability to Recover the Size of Stack Buffers</h4><p>经过编译优化,下面的代码中s1由于是指向了为初始化内存,所以可能会报告成栈溢出</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220912142153011.png" alt="image-20220912142153011"><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220912142204926.png" alt="image-20220912142204926"></p><p>说是这样说,但是我个人认为,这种情况是可以避免的,通过汇编是可以判断出来这个 stack buffer 有多大,这种误报理论上是可以排出的,前提是收集更多的信息 <– 优化项</p><h4 id="P2-Signed-and-Unsigned-Integers"><a href="#P2-Signed-and-Unsigned-Integers" class="headerlink" title="P2 - Signed and Unsigned Integers"></a>P2 - Signed and Unsigned Integers</h4><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220912142520350.png" alt="image-20220912142520350"></p><p>没什么好说的,变量类型分析错误,在对伪代码中产生误报正常,这个如果不人为干预,确实没办法</p><h4 id="P3-Integer-Operations-on-Uninitialized-Variables"><a href="#P3-Integer-Operations-on-Uninitialized-Variables" class="headerlink" title="P3 - Integer Operations on Uninitialized Variables"></a>P3 - Integer Operations on Uninitialized Variables</h4><p>由于缺少必要的信息,导致在 <code>sub 129CF</code>丢失了a2和a4的信息,导致SAST工具产生误报</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220912142633914.png" alt="image-20220912142633914"></p><h4 id="P4-Function-Pointers"><a href="#P4-Function-Pointers" class="headerlink" title="P4 - Function Pointers"></a>P4 - Function Pointers</h4><p>函数指针问题,这个很巧合,前两天请教<code>@jmpews</code>的时候,提到了decompiler+SAST的做法,当时我问的是 <code>joern</code>,大佬的说法是C更不好整, <code>c++ 还可以走 demangle</code></p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220912142958000.png" alt="image-20220912142958000"></p><p>上面的代码反编译之后得到:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220912143018996.png" alt="image-20220912143018996"></p><p>基本上是无法分析的,就算是人肉逆向,也要重建这个结构体,然后转换变量类型,自动化不太现实,这块的误报确实没办法</p><h4 id="P5-Pointers-as-Integers"><a href="#P5-Pointers-as-Integers" class="headerlink" title="P5 - Pointers as Integers"></a>P5 - Pointers as Integers</h4><p>还是变量类型的问题,反编译代码对特定变量类型分析错误,导致的误报</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220912143157818.png" alt="image-20220912143157818"></p><p>反编译之后:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220912143214154.png" alt="image-20220912143214154"></p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220912143233125.png" alt="image-20220912143233125"></p><h4 id="P6-Integers-of-Wrong-Size"><a href="#P6-Integers-of-Wrong-Size" class="headerlink" title="P6 - Integers of Wrong Size"></a>P6 - Integers of Wrong Size</h4><p>对v22 和 v26 类型识别错误,混用了 <code>uint8_t</code> 和 <code>int64</code>,所以可能会误报整数溢出 :(</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220912143334876.png" alt="image-20220912143334876"></p><h4 id="P7-Simplified-Expressions"><a href="#P7-Simplified-Expressions" class="headerlink" title="P7 - Simplified Expressions"></a>P7 - Simplified Expressions</h4><p>这是一类特殊情况</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220912143520029.png" alt="image-20220912143520029"></p><p>这里显然把<code>||</code>和<code>&&</code>搞混了,但是反编译之后</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220912143546104.png" alt="image-20220912143546104"></p><p>这类表达式在编译器优化处理之后,再反编译,该表达式已经看不出来了,就会漏报这个问题 :(</p><h2 id="Futher-work"><a href="#Futher-work" class="headerlink" title="Futher work"></a>Futher work</h2><p><code>decompiler+SAST</code>可行,但是需要优化,能覆盖的场景也有限,目前来看IoT场景是比较适合的,比如各种奇葩的命令注入,显然是可以召回的。</p><ol><li> 最好不要选依赖编译的SAST工具,如codeql</li><li> 选择可以自定义规则的工具</li><li> 最好可以支持过程间分析、数据流</li><li> 为了弥补反编译代码的不足,可以结合汇编层面收集一些信息,比如栈上变量的大小</li></ol><blockquote><p> 个人来一个大胆的构想,从ctree上收集信息,生成codeql那样的rel db,目前来看比较接近的是joern,但是它是基于ghirda,优化空间还是有的。</p></blockquote><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p><a href="https://dl.acm.org/doi/10.1145/3488932.3497764">https://dl.acm.org/doi/10.1145/3488932.3497764</a></p><p><a href="https://security.humanativaspa.it/automating-binary-vulnerability-discovery-with-ghidra-and-semgrep/">https://security.humanativaspa.it/automating-binary-vulnerability-discovery-with-ghidra-and-semgrep/</a></p><p><a href="https://docs.joern.io/home">https://docs.joern.io/home</a></p>]]></content>
<summary type="html"><h2 id="Background"><a href="#Background" class="headerlink" title="Background"></a><code>Background</code></h2><p> 最近阅读了一篇论文<code>&lt;&lt;The Convergence of Source Code and Binary Vulnerability Discovery – A Case Study&gt;&gt;</code>,很巧合的是论文的研究中,关于将SAST工具应用于二进制文件(通过decompiler),即获取伪代码之后,在伪代码上跑SAST工具来找漏洞这个模式我和<code>@C0ss4ck</code>一起做过,在我们收到一些成效之后发现也有人做了<a href="https://security.humanativaspa.it/automating-binary-vulnerability-discovery-with-ghidra-and-semgrep/">类似的工作</a>,不过他好像没有特别深入 :D </p></summary>
<category term="论文阅读" scheme="https://o0xmuhe.github.io/categories/%E8%AE%BA%E6%96%87%E9%98%85%E8%AF%BB/"/>
<category term="Paper" scheme="https://o0xmuhe.github.io/tags/Paper/"/>
</entry>
<entry>
<title>HW OTA unpack</title>
<link href="https://o0xmuhe.github.io/2022/09/02/HW-OTA-unpack/"/>
<id>https://o0xmuhe.github.io/2022/09/02/HW-OTA-unpack/</id>
<published>2022-09-02T05:43:15.000Z</published>
<updated>2022-11-13T08:00:43.423Z</updated>
<content type="html"><![CDATA[<h2 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h2><h3 id="unzip解开OTA包"><a href="#unzip解开OTA包" class="headerlink" title="unzip解开OTA包"></a>unzip解开OTA包</h3><span id="more"></span><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220825111641389.png" alt="image-20220825111641389"></p><p>我们的目标在 update_sd_base.zip里,其他部分咨询了是一些出厂带的APP,比如里面就看到了今日头条 抖音啥的。</p><p>直接解开 update_sd_base.zip 到下一步</p><h3 id="从UPDATE-APP提取SYSTEM"><a href="#从UPDATE-APP提取SYSTEM" class="headerlink" title="从UPDATE.APP提取SYSTEM"></a>从UPDATE.APP提取SYSTEM</h3><p>直接用 <a href="https://github.com/jenkins-84/split_updata.pl/blob/master/splitupdate">https://github.com/jenkins-84/split_updata.pl/blob/master/splitupdate</a> 来分割就行</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220825111723396.png" alt="image-20220825111723396"></p><h3 id="unpack-erofs"><a href="#unpack-erofs" class="headerlink" title="unpack erofs"></a>unpack erofs</h3><h4 id="方法1-simg2img然后挂在erofs-kernel-5-4"><a href="#方法1-simg2img然后挂在erofs-kernel-5-4" class="headerlink" title="方法1: simg2img然后挂在erofs(kernel 5.4)"></a>方法1: simg2img然后挂在erofs(kernel 5.4)</h4><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">~/android-simg2img/simg2img SYSTEM.img system1.img</span><br><span class="line">sudo mount -t erofs system1.img 1 -oloop</span><br></pre></td></tr></table></figure><p>尝试读文件的时候发现报错</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220825111833524.png" alt="image-20220825111833524"></p><p>dmesg发现 : </p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220825111906816.png" alt="image-20220825111906816"></p><h4 id="方法2-直接使用-extractor里的-erofs-tools-py-来直接把system镜像解开"><a href="#方法2-直接使用-extractor里的-erofs-tools-py-来直接把system镜像解开" class="headerlink" title="方法2: 直接使用 extractor里的 erofs_tools.py 来直接把system镜像解开"></a>方法2: 直接使用 extractor里的 erofs_tools.py 来直接把system镜像解开</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">~/extracotr/erofs_tool.py extract --verify-zip system1.img harmony_system</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://zhuanlan.zhihu.com/p/60617375">https://zhuanlan.zhihu.com/p/60617375</a></p><p><a href="https://github.com/jenkins-84/split_updata.pl">https://github.com/jenkins-84/split_updata.pl</a></p><p><a href="https://github.com/srlabs/extractor">https://github.com/srlabs/extractor</a></p>]]></content>
<summary type="html"><h2 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h2><h3 id="unzip解开OTA包"><a href="#unzip解开OTA包" class="headerlink" title="unzip解开OTA包"></a>unzip解开OTA包</h3></summary>
<category term="Android" scheme="https://o0xmuhe.github.io/categories/Android/"/>
<category term="EROFS" scheme="https://o0xmuhe.github.io/tags/EROFS/"/>
</entry>
<entry>
<title>optee学习篇(1) 环境&调试</title>
<link href="https://o0xmuhe.github.io/2022/08/24/optee%E5%AD%A6%E4%B9%A0/"/>
<id>https://o0xmuhe.github.io/2022/08/24/optee%E5%AD%A6%E4%B9%A0/</id>
<published>2022-08-24T15:15:33.000Z</published>
<updated>2022-11-13T07:57:29.564Z</updated>
<content type="html"><![CDATA[<h2 id="环境配置"><a href="#环境配置" class="headerlink" title="环境配置"></a>环境配置</h2><p>本篇主要是环境配置、调试、流程梳理</p><span id="more"></span><h3 id="qemu-v8环境搭建"><a href="#qemu-v8环境搭建" class="headerlink" title="qemu_v8环境搭建"></a>qemu_v8环境搭建</h3><blockquote><p><a href="https://optee.readthedocs.io/en/latest/building/prerequisites.html">https://optee.readthedocs.io/en/latest/building/prerequisites.html</a></p></blockquote><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">$ repo init -u https://github.com/OP-TEE/manifest.git -m qemu_v8.xml</span><br><span class="line">$ repo sync -c -j8</span><br><span class="line">$ <span class="built_in">cd</span> build</span><br><span class="line">$ make toolchains</span><br><span class="line">$ make run</span><br></pre></td></tr></table></figure><p>同步下来的仓库如下</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220822175933034.png" alt="image-20220822175933034"></p><p>运行一下试试看:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220824161556316.png" alt="image-20220824161556316"></p><p>需要指定版本跑的话 : <code>make -f qemu_v8.mk run-only</code></p><blockquote><p><a href="https://optee.readthedocs.io/en/latest/debug/index.html">https://optee.readthedocs.io/en/latest/debug/index.html</a></p></blockquote><p>这里以qemu-v8为例</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"><span class="built_in">cd</span> build</span><br><span class="line">make DEBUG=1 -f qemu_v8.mk all</span><br></pre></td></tr></table></figure><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">make DEBUG=1 -f qemu_v8.mk run-only</span><br></pre></td></tr></table></figure><p>因为Makefile中启动的时候已经写了设置了 <code>-s -S</code>了 ,所以可以直接连接</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220824163338069.png" alt="image-20220824163338069"></p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220824163032421.png" alt="image-20220824163032421"></p><p>可以从<a href="https://developer.arm.com/downloads/-/gnu-a">这里</a>下载对应的gdb来用</p><p>装了<code>libncurses5-dev</code>还是找不到so的话,可以参考 <a href="https://www.cnblogs.com/wanglouxiaozi/p/14987053.html">https://www.cnblogs.com/wanglouxiaozi/p/14987053.html</a></p><p><code>gdb-multiarch</code> 也可以,更好用</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220824174332829.png" alt="image-20220824174332829"></p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220824174546625.png" alt="image-20220824174546625"></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></pre></td><td class="code"><pre><span class="line">bl1 --- /home/work/optee/trusted-firmware-a/build/qemu/debug/bl1/bl1.elf</span><br><span class="line">bl2 --- /home/work/optee/trusted-firmware-a/build/qemu/debug/bl2/bl2.elf</span><br><span class="line">bl31 --- /home/work/optee/trusted-firmware-a/build/qemu/debug/bl31/bl31.elf </span><br><span class="line">bl32(teeOS) ---- /home/work/optee/optee_os/out/arm/core/tee.elf</span><br><span class="line">bl33(UEFI) --- /home/work/optee/edk2/Build/ </span><br></pre></td></tr></table></figure><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220824175034184.png" alt="image-20220824175034184"></p><h3 id="fvp可视化调试环境搭建"><a href="#fvp可视化调试环境搭建" class="headerlink" title="fvp可视化调试环境搭建"></a>fvp可视化调试环境搭建</h3><h4 id="代码获取"><a href="#代码获取" class="headerlink" title="代码获取"></a>代码获取</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></pre></td><td class="code"><pre><span class="line">$ repo init -u https://github.com/OP-TEE/manifest.git -m fvp.xml </span><br><span class="line">$ repo sync -j4 -c</span><br><span class="line">Updating depot_tools...</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="工具链"><a href="#工具链" class="headerlink" title="工具链"></a>工具链</h4><p>需要下载 <code>FVP_Base_RevC-2xAEMvA_11.18_16_Linux64.tgz</code>并解压到<code>optee-fvp</code>目录下</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">$ tar -zxvf ../FVP_Base_RevC-2xAEMvA_11.18_16_Linux64.tgz -C .</span><br><span class="line"></span><br></pre></td></tr></table></figure><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><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># work @ work-virtual-machine in ~/optee-fvp [23:34:36] </span></span><br><span class="line">$ ls -al</span><br><span class="line">total 72</span><br><span class="line">drwxrwxr-x 18 work work 4096 Sep 22 23:32 .</span><br><span class="line">drwx------ 70 work work 4096 Sep 22 23:34 ..</span><br><span class="line">drwxrwxr-x 10 work work 4096 Sep 22 23:32 Base_RevC_AEMvA_pkg</span><br><span class="line">drwxrwxr-x 12 work work 4096 Sep 22 23:26 build</span><br><span class="line">drwxrwxr-x 14 work work 4096 Sep 22 23:26 buildroot</span><br><span class="line">drwxrwxr-x 50 work work 4096 Sep 22 23:26 edk2</span><br><span class="line">drwxrwxr-x 4 work work 4096 Sep 22 23:26 edk2-platforms</span><br><span class="line">drwxrwxr-x 14 work work 4096 Sep 22 23:26 grub</span><br><span class="line">drwxr-xr-x 3 work work 4096 Jun 16 10:34 license_terms</span><br><span class="line">drwxrwxr-x 24 work work 4096 Sep 22 23:26 linux</span><br><span class="line">drwxrwxr-x 14 work work 4096 Sep 22 23:26 mbedtls</span><br><span class="line">drwxrwxr-x 5 work work 4096 Sep 22 23:26 ms-tpm-20-ref</span><br><span class="line">drwxrwxr-x 9 work work 4096 Sep 22 23:26 optee_client</span><br><span class="line">drwxrwxr-x 10 work work 4096 Sep 22 23:26 optee_examples</span><br><span class="line">drwxrwxr-x 10 work work 4096 Sep 22 23:26 optee_os</span><br><span class="line">drwxrwxr-x 7 work work 4096 Sep 22 23:26 optee_test</span><br><span class="line">drwxrwxr-x 7 work work 4096 Sep 22 23:26 .repo</span><br><span class="line">drwxrwxr-x 19 work work 4096 Sep 22 23:26 trusted-firmware-a</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="编译"><a href="#编译" class="headerlink" title="编译"></a>编译</h4><blockquote><p>编译流程参考上面qemu_v8部分</p></blockquote><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"><span class="comment"># work @ work-virtual-machine in ~/optee-fvp [23:35:06]</span></span><br><span class="line">$ cp -rf Base_RevC_AEMvA_pkg Foundation_Platformpkg <span class="comment"># build toolchains的时候文件夹名需要改一下</span></span><br><span class="line">$ <span class="built_in">cd</span> build</span><br><span class="line">$ make toolchains</span><br><span class="line">$ make DEBUG=1 FVP_USE_BASE_PLAT=y -f fvp.mk all </span><br></pre></td></tr></table></figure><h4 id="开启调试"><a href="#开启调试" class="headerlink" title="开启调试"></a>开启调试</h4><p>修改build/fvp.mk ,以便启动时进入调试模式</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></pre></td><td class="code"><pre><span class="line">-I \</span><br><span class="line">--iris-allow-remote\</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="comment">################################################################################</span></span><br><span class="line"><span class="comment"># Run targets</span></span><br><span class="line"><span class="comment">################################################################################</span></span><br><span class="line"><span class="comment"># This target enforces updating root fs etc</span></span><br><span class="line">run: all</span><br><span class="line"> $(MAKE) run-only</span><br><span class="line"></span><br><span class="line">ifeq ($(FVP_USE_BASE_PLAT),y)</span><br><span class="line">FVP_ARGS ?= \</span><br><span class="line"> -I \</span><br><span class="line"> --iris-allow-remote\</span><br><span class="line"> -C bp.ve_sysregs.exit_on_shutdown=1 \</span><br><span class="line"> -C cache_state_modelled=0 \</span><br><span class="line"> -C pctl.startup=0.0.0.0 \</span><br><span class="line"> -C cluster0.NUM_CORES=4 \</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="运行"><a href="#运行" class="headerlink" title="运行"></a>运行</h4><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"><span class="built_in">cd</span> build </span><br><span class="line">make DEBUG=1 FVP_USE_BASE_PLAT=y -f fvp.mk run-only</span><br></pre></td></tr></table></figure><h4 id="ARM-Developer-Studio连接"><a href="#ARM-Developer-Studio连接" class="headerlink" title="ARM Developer Studio连接"></a>ARM Developer Studio连接</h4><p>启动的时候需要license,注册个账号就行,先试用。</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220924200021216.png" alt="image-20220924200021216"></p><p>启动之后,选择 : <code>File->New->Model Connection </code></p><p>模型选择 : Base_RevC_AEMvA 和 <code>Base_RevC_AEMvA</code> 都没法直接调试,好像是模型没装好 :(</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220924200407393.png" alt="image-20220924200407393"></p><p>点Finish之后,需要手动选择,连接本地的模型 <code>localhost 7100</code></p><p>加载了bl31的符号,然后对入口下断: </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">b *EL3:0x0000000004003000</span><br><span class="line"></span><br><span class="line">add-symbol-file /home/muhe/Study/optee-fvp/trusted-firmware-a/build/fvp/debug/bl31/bl31.elf</span><br></pre></td></tr></table></figure><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220924204537245.png" alt="image-20220924204537245"></p><p>芜湖 🛫️ </p><h2 id="ARM-安全架构"><a href="#ARM-安全架构" class="headerlink" title="ARM 安全架构"></a>ARM 安全架构</h2><blockquote><p>ARM v8 的文档</p></blockquote><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220825135100483.png" alt="image-20220825135100483"></p><ul><li>分三个异常等级</li><li>两个“世界”,non-secure 和 secure</li></ul><p><code>opteeos</code>跑在<code> secure world</code>,<code>ta</code>在<code>secure world</code>的上层(<code>el0</code>); <code>linux</code>在<code>non-secure world</code>,<code>ca</code>在<code>el0</code></p><p><code>optee</code>项目中还有个``atf<code>,这个跑在</code>el3`。</p><blockquote><p> 该图来自<a href="https://edu.csdn.net/lecturer/6964">周贺贺老师的OPTEE系列</a>课程中</p></blockquote><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220825135607360.png" alt="image-20220825135607360"></p><h2 id="OPTEE-启动流程"><a href="#OPTEE-启动流程" class="headerlink" title="OPTEE 启动流程"></a>OPTEE 启动流程</h2><p>这里我直接用了<a href="https://edu.csdn.net/lecturer/6964">周贺贺老师OPTEE系列课程中</a>的图,我在对着代码分析的时候结合这个图感觉十分的清晰,有助于理解 :)</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220901215718045.png" alt="image-20220901215718045"></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><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><br><span class="line"></span><br><span class="line">bl31_entrypoint (trusted-firmware-a/bl31/aarch64/bl31_entrypoint.S)</span><br><span class="line"> bl31_main (trusted-firmware-a/bl31/bl31_main.c)</span><br><span class="line"> runtime_svc_init (trusted-firmware-a/common/runtime_svc.c)</span><br><span class="line"> opteed_setup (trusted-firmware-a/services/spd/opteed/opteed_main.c DECLARE_RT_SVC里定义)</span><br><span class="line"> bl31_plat_get_next_image_ep_info(SECURE) </span><br><span class="line"> opteed_init_optee_ep_state</span><br><span class="line"> bl31_register_bl32_init(&opteed_init);</span><br><span class="line"></span><br><span class="line"> bl32_init // 这个函数就是opteed_init,在上面注册的 </span><br><span class="line"> // 进入tee</span><br><span class="line"> opteed_synchronous_sp_entry(optee_ctx);</span><br><span class="line"> opteed_enter_sp(&optee_ctx->c_rt_ctx);</span><br><span class="line"> // 出tee</span><br><span class="line"> bl31_prepare_next_image_entry (进入uboot)</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><blockquote><p>下断点的時候注意,切换到对应的阶段之后再去 file xxx 加载符号</p></blockquote><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">file /home/work/optee/trusted-firmware-a/build/qemu/debug/bl1/bl1.elf</span><br><span class="line">file /home/work/optee/trusted-firmware-a/build/qemu/debug/bl2/bl2.elf</span><br><span class="line">file /home/work/optee/trusted-firmware-a/build/qemu/debug/bl31/bl31.elf </span><br><span class="line">file /home/work/optee/optee_os/out/arm/core/tee.elf</span><br></pre></td></tr></table></figure><p>比如下面要进入tee的时候</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/from_atf_to_tee.png" alt="ready_enter_tee"></p><p>加载符号之后: </p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/tee.png" alt="tee"></p><h3 id="ads可视化流程梳理"><a href="#ads可视化流程梳理" class="headerlink" title="ads可视化流程梳理"></a>ads可视化流程梳理</h3><blockquote><p> ads可视化调试记录</p></blockquote><h4 id="bl1"><a href="#bl1" class="headerlink" title="bl1"></a>bl1</h4><blockquote><p> add-symbol-file /home/muhe/Study/optee-fvp/trusted-firmware-a/build/fvp/debug/bl1/bl1.elf</p></blockquote><h4 id="bl2"><a href="#bl2" class="headerlink" title="bl2"></a>bl2</h4><blockquote><p> b *EL1S:0x0000000004022000 </p></blockquote><blockquote><p> add-symbol-file /home/muhe/Study/optee-fvp/trusted-firmware-a/build/fvp/debug/bl2/bl2.elf</p></blockquote><h4 id="bl31"><a href="#bl31" class="headerlink" title="bl31"></a>bl31</h4><blockquote><p> b *EL3:0x0000000004003000</p></blockquote><blockquote><p> add-symbol-file /home/muhe/Study/optee-fvp/trusted-firmware-a/build/fvp/debug/bl31/bl31.elf</p></blockquote><h4 id="bl32"><a href="#bl32" class="headerlink" title="bl32"></a>bl32</h4><blockquote><p> b *EL1S:0x6000000</p><p> add-symbol-file /home/muhe/Study/optee-fvp/optee_os/out/arm/core/tee.elf</p></blockquote><p>UEFI 的符号加载比较特殊, 这个部分是相对地址, 并且很多模块是动态加载的, 断点下到加载UEFI的地址, 也就是BL31 跳转到BL33时的地址。 断下后,</p><p>commands下通过 cmd_load_symbols 加载, 执行前先要弄清楚几个参数 </p><blockquote><p>/home/muhe/Study/optee-fvp/edk2/ArmPlatformPkg/Scripts/Ds5/cmd_load_symbols.py</p></blockquote><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></pre></td><td class="code"><pre><span class="line">def usage():</span><br><span class="line"> print "-v,--verbose"</span><br><span class="line"> print "-a,--all: Load all symbols"</span><br><span class="line"> print "-l,--report=: Filename for the EDK2 report log"</span><br><span class="line"> print "-m,--sysmem=(base,size): System Memory region"</span><br><span class="line"> print "-f,--fv=(base,size): Firmware region"</span><br><span class="line"> print "-r,--rom=(base,size): ROM region"</span><br></pre></td></tr></table></figure><p>-m 参数在 </p><blockquote><p> /home/muhe/Study/optee-fvp/edk2-platforms/Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.dsc</p></blockquote><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"># <span class="function">System <span class="title">Memory</span> <span class="params">(<span class="number">2</span>GB - <span class="number">16</span>MB of Trusted DRAM at the top of the <span class="number">32b</span>it address space)</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">gArmTokenSpaceGuid.PcdSystemMemoryBase|0x80000000</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">gArmTokenSpaceGuid.PcdSystemMemorySize|0x7F000000</span></span><br></pre></td></tr></table></figure><p>-f 参数在 </p><blockquote><p> /home/muhe/Study/optee-fvp/edk2-platforms/Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.fdf </p></blockquote><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">[FD.FVP_AARCH64_EFI]</span><br><span class="line"></span><br><span class="line">!ifdef ARM_FVP_RUN_NORFLASH</span><br><span class="line"></span><br><span class="line">BaseAddress = <span class="number">0x08000000</span>|gArmTokenSpaceGuid.PcdFdBaseAddress # The base address of the Firmware in Flash0.</span><br><span class="line"></span><br><span class="line">!<span class="keyword">else</span></span><br><span class="line"></span><br><span class="line">BaseAddress = <span class="number">0x88000000</span>|gArmTokenSpaceGuid.PcdFdBaseAddress # UEFI in DRAM + <span class="number">128</span>MB.</span><br><span class="line"></span><br><span class="line">!endif</span><br><span class="line"></span><br><span class="line">Size = <span class="number">0x04000000</span>|gArmTokenSpaceGuid.PcdFdSize # The size in bytes of the device (<span class="number">64</span>MiB).</span><br><span class="line"></span><br><span class="line">ErasePolarity = <span class="number">1</span></span><br></pre></td></tr></table></figure><h4 id="bl33"><a href="#bl33" class="headerlink" title="bl33"></a>bl33</h4><h4 id="UEFI"><a href="#UEFI" class="headerlink" title="UEFI"></a>UEFI</h4><blockquote><p> b *EL2N:0x88000000</p></blockquote><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"><span class="built_in">source</span> /home/muhe/Study/optee-fvp/edk2/ArmPlatformPkg/Scripts/Ds5/cmd_load_symbols.py -a -m (0x80000000, 0x7F000000) -f (0x88000000, 0x04000000) </span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">info files</span><br><span class="line">Symbols from <span class="string">"/home/muhe/Study/optee-fvp/edk2-platforms/Build/ArmVExpress-FVP-AArch64/DEBUG_GCC49/AARCH64/ArmPlatformPkg/PrePi/PeiUniCore/DEBUG/ArmPlatformPrePiUniCore.dll"</span>.</span><br><span class="line">Local <span class="built_in">exec</span> file:</span><br><span class="line"> <span class="string">"/home/muhe/Study/optee-fvp/edk2-platforms/Build/ArmVExpress-FVP-AArch64/DEBUG_GCC49/AARCH64/ArmPlatformPkg/PrePi/PeiUniCore/DEBUG/ArmPlatformPrePiUniCore.dll"</span>, file <span class="built_in">type</span> ELF64.</span><br><span class="line"> Entry point: EL2N:0x0000000088000800.</span><br><span class="line"> EL2N:0x0000000088000800 - EL2N:0x0000000088018AD7 is .text</span><br><span class="line"> EL2N:0x0000000088019000 - EL2N:0x000000008801916F is .data</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>DxeCore的加载这个脚本处理不了,还是要自己加载</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">add-symbol-file /home/muhe/Study/optee-fvp/edk2-platforms/Build/ArmVExpress-FVP-AArch64/DEBUG_GCC49/AARCH64/MdeModulePkg/Core/Dxe/DxeMain/DEBUG/DxeCore.dll 0x00fe3d3000</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><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">Symbols from <span class="string">"/home/muhe/Study/optee-fvp/edk2-platforms/Build/ArmVExpress-FVP-AArch64/DEBUG_GCC49/AARCH64/ArmPlatformPkg/PrePi/PeiUniCore/DEBUG/ArmPlatformPrePiUniCore.dll"</span>.</span><br><span class="line">Local <span class="built_in">exec</span> file:</span><br><span class="line"> <span class="string">"/home/muhe/Study/optee-fvp/edk2-platforms/Build/ArmVExpress-FVP-AArch64/DEBUG_GCC49/AARCH64/ArmPlatformPkg/PrePi/PeiUniCore/DEBUG/ArmPlatformPrePiUniCore.dll"</span>, file <span class="built_in">type</span> ELF64.</span><br><span class="line"> Entry point: EL2N:0x0000000088000800.</span><br><span class="line"> EL2N:0x0000000088000800 - EL2N:0x0000000088018AD7 is .text</span><br><span class="line"> EL2N:0x0000000088019000 - EL2N:0x000000008801916F is .data</span><br><span class="line">Symbols from <span class="string">"/home/muhe/Study/optee-fvp/edk2-platforms/Build/ArmVExpress-FVP-AArch64/DEBUG_GCC49/AARCH64/MdeModulePkg/Core/Dxe/DxeMain/DEBUG/DxeCore.dll"</span>.</span><br><span class="line">Local <span class="built_in">exec</span> file:</span><br><span class="line"> <span class="string">"/home/muhe/Study/optee-fvp/edk2-platforms/Build/ArmVExpress-FVP-AArch64/DEBUG_GCC49/AARCH64/MdeModulePkg/Core/Dxe/DxeMain/DEBUG/DxeCore.dll"</span>, file <span class="built_in">type</span> ELF64.</span><br><span class="line"> Entry point: EL2N:0x00000000FE3D4000.</span><br><span class="line"> EL2N:0x00000000FE3D4000 - EL2N:0x00000000FE41AEBF is .text</span><br><span class="line"> EL2N:0x00000000FE41B000 - EL2N:0x00000000FE435860 is .data</span><br></pre></td></tr></table></figure><h4 id="GRUB"><a href="#GRUB" class="headerlink" title="GRUB"></a>GRUB</h4><p>TODO,这部分一直没时间搞,先挂起了</p><h4 id="Linux-Kernel"><a href="#Linux-Kernel" class="headerlink" title="Linux Kernel"></a>Linux Kernel</h4><blockquote><p> BL33是UEFl,其实UEFI 还会引导grub2, 这里grub2作为一个UEFl的driver(or 应用)被UEFl加载, grub执行完毕,引导linux时,其实linux 内核也打包作为一个UEFl的应用了,所以BL33的执行过程是, UEFI-> grub->linux内核的efi stub -> linux内核</p></blockquote><p>加载符号,注意EL1N:0,因为内核主要运行在EL1N</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">add-symbol-file /home/muhe/Study/optee-fvp/linux/vmlinux EL1N:0</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></pre></td><td class="code"><pre><span class="line">__HEAD</span><br><span class="line"> primary_entry</span><br><span class="line"> __primary_switch</span><br><span class="line"> __enable_mmu</span><br><span class="line"> __primary_switched</span><br><span class="line"> 设置异常向量表 // `adr_l x8, vectors`</span><br><span class="line"> start_kernel()</span><br></pre></td></tr></table></figure><p>我们可以对<code>__primary_switch</code> 下断,如果符号对不上,可以根据地址下断</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"><span class="comment"># muhe @ muhe-NUC11PAHi5 in ~/Study/optee-fvp/linux on git:29aee39cf x [23:24:42]</span></span><br><span class="line">$ cat System.map | grep <span class="string">"primary_switched"</span></span><br><span class="line">ffff80000919032c t __primary_switched</span><br></pre></td></tr></table></figure><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/debug_linux_kernel.png" alt="debug_linux_kernel"></p><h4 id="JVM设置"><a href="#JVM设置" class="headerlink" title="JVM设置"></a>JVM设置</h4><p>修改ads的jvm,否则调试的时候容易oom影响体验</p><blockquote><p><a href="https://developer.arm.com/documentation/ka003567/latest">https://developer.arm.com/documentation/ka003567/latest</a></p></blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># muhe @ muhe-NUC11PAHi5 in /usr/arm/developmentstudio-2022.1/sw/ide [20:20:40]</span></span><br><span class="line">$ cat armds_ide.ini</span><br><span class="line">-startup</span><br><span class="line">plugins/org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar</span><br><span class="line">--launcher.library</span><br><span class="line">plugins/org.eclipse.equinox.launcher.gtk.linux.x86_64_1.2.400.v20211117-0650</span><br><span class="line">-vm</span><br><span class="line">../java/lib/server/libjvm.so</span><br><span class="line">-vmargs</span><br><span class="line">--add-opens=java.base/java.io=ALL-UNNAMED</span><br><span class="line">--add-opens=java.base/sun.nio.ch=ALL-UNNAMED</span><br><span class="line">--add-opens=java.base/java.lang=ALL-UNNAMED</span><br><span class="line">--add-opens=java.base/java.util=ALL-UNNAMED</span><br><span class="line">--add-opens=java.base/java.nio.charset=ALL-UNNAMED</span><br><span class="line">--add-opens=java.base/java.nio=ALL-UNNAMED</span><br><span class="line">--add-opens=java.base/java.lang.reflect=ALL-UNNAMED</span><br><span class="line">-Dnashorn.args=--no-deprecation-warning</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><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">-Xms4096m </span><br><span class="line">-Xmx4096m </span><br><span class="line">-Xmn256m</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://download.csdn.net/course/detail/37655">https://download.csdn.net/course/detail/37655</a></p><p><a href="https://optee.readthedocs.io/en/latest/">https://optee.readthedocs.io/en/latest/</a></p><p><a href="https://armv8-ref.codingbelief.com/zh/">https://armv8-ref.codingbelief.com/zh/</a></p><p><a href="https://edu.csdn.net/lecturer/6964">https://edu.csdn.net/lecturer/6964</a></p>]]></content>
<summary type="html"><h2 id="环境配置"><a href="#环境配置" class="headerlink" title="环境配置"></a>环境配置</h2><p>本篇主要是环境配置、调试、流程梳理</p></summary>
<category term="optee" scheme="https://o0xmuhe.github.io/categories/optee/"/>
<category term="optee" scheme="https://o0xmuhe.github.io/tags/optee/"/>
</entry>
<entry>
<title>weggli debug</title>
<link href="https://o0xmuhe.github.io/2022/07/24/weggli-debug/"/>
<id>https://o0xmuhe.github.io/2022/07/24/weggli-debug/</id>
<published>2022-07-24T01:19:32.000Z</published>
<updated>2022-11-13T07:57:09.611Z</updated>
<content type="html"><![CDATA[<h2 id="关于Weggli"><a href="#关于Weggli" class="headerlink" title="关于Weggli"></a>关于Weggli</h2><blockquote><p>AST Pattern Search</p></blockquote><p>核心是使用和 <code>tree-sitter</code> 库,然后搞了 <code>query-tree</code> 来在 <code>AST</code>上进行搜索,这只能说是匹配特定的代码片段,还达不到程序分析的那个级别,所以理论上只能过程内分析,而且没有上下文啥的 :D 直白点说的话,像是<code>AST</code>的正则表达式,不过某种意义上来说对于使用白盒方案快速召回一些漏洞也是一种借鉴吧。</p><span id="more"></span><p>当然我也用这个工具做了一些扩展,结合其他工具解决了一些问题,目前看来这个东西还是具有一定的可玩性的 :D </p><h2 id="Weggli如何工作"><a href="#Weggli如何工作" class="headerlink" title="Weggli如何工作"></a>Weggli如何工作</h2><blockquote><p>看代码,调试分析</p></blockquote><h3 id="idea配置"><a href="#idea配置" class="headerlink" title="idea配置"></a>idea配置</h3><p>安装<code>Rust</code>插件,调试的话,会默认再去安装<code>Native Debugging Support</code>,有了这俩东西就可以调试了</p><p>配置传递给weggli的参数的话跟在 <code>--</code> 后面即可 : </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">run --package weggli --bin weggli -- <span class="string">"{<span class="variable">$func</span>(<span class="variable">$b</span>);system(<span class="variable">$b</span>);}"</span> -R <span class="string">"func=printf$"</span> /path/to/src</span><br></pre></td></tr></table></figure><h3 id="工作流程"><a href="#工作流程" class="headerlink" title="工作流程"></a>工作流程</h3><blockquote><p>只描述核心流程</p></blockquote><h4 id="query-tree-构建"><a href="#query-tree-构建" class="headerlink" title="query-tree 构建"></a>query-tree 构建</h4><p>参考 <a href="https://tree-sitter.github.io/tree-sitter/">tree-sitter文档</a></p><figure class="highlight rust"><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="keyword">let</span> work: <span class="built_in">Vec</span><WorkItem> = args</span><br><span class="line"> .pattern</span><br><span class="line"> .iter()</span><br><span class="line"> .map(|pattern| {</span><br><span class="line"> <span class="keyword">match</span> parse_search_pattern(pattern, args.cpp, args.force_query, &regex_constraints) {</span><br><span class="line"> <span class="literal">Ok</span>(qt) => {</span><br><span class="line"> <span class="keyword">let</span> identifiers = qt.identifiers();</span><br><span class="line"> variables.extend(qt.variables());</span><br><span class="line"> WorkItem { qt, identifiers }</span><br><span class="line"> <span class="comment">// ....</span></span><br></pre></td></tr></table></figure><p>构造 <code>WorkItem{qt, identifiers}</code></p><ul><li>qt : query-tree, tree-sitter的Tree对象</li><li>identifiers : 标识符,query中”终结符” </li></ul><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></pre></td><td class="code"><pre><span class="line">main</span><br><span class="line"> parse_search_pattern</span><br><span class="line"> weggli::parse(pattern, is_cpp) // 返回Tree对象</span><br><span class="line"> //修正pattern</span><br><span class="line"> validate_query</span><br><span class="line"> build_query_tree</span><br></pre></td></tr></table></figure><blockquote><p> 修正pattern : weggli处理了“不合法的”格式,如:</p></blockquote><ul><li><code>memcpy(a,b,size)</code> -> <code>memcpy(a,b,size);</code></li><li><code>memcpy(a,b,size);</code> -> <code>{memcpy(a,b,size);}</code></li></ul><blockquote><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">validate_query(&tree, p, force_query)? <span class="comment">// 返回 TreeCursor对象,用于遍历AST</span></span><br></pre></td></tr></table></figure></blockquote><p>语法合法性检查,如果 <code>force_query</code>为<code>True</code>,意味着忽略这些语法错误</p><p>如 : </p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220707154439731.png" alt="image-20220707154439731"></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">"{$func($b);_($b);}"</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></pre></td><td class="code"><pre><span class="line">(translation_unit </span><br><span class="line"> (</span><br><span class="line"> compound_statement </span><br><span class="line"> (</span><br><span class="line"> expression_statement (call_expression function: (identifier) arguments: (argument_list (identifier)))</span><br><span class="line"> ) </span><br><span class="line"> (</span><br><span class="line"> expression_statement (call_expression function: (identifier) arguments: (argument_list (identifier)))</span><br><span class="line"> )</span><br><span class="line"> )</span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>同时还不允许 : </p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220707155732813.png" alt="image-20220707155732813"></p><p>返回的是 : <code>c.goto_first_child();</code>,即 花括号中间的内容</p><blockquote><figure class="highlight rust"><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="literal">Ok</span>(build_query_tree(</span><br><span class="line"> p,</span><br><span class="line"> &<span class="keyword">mut</span> c,</span><br><span class="line"> is_cpp,</span><br><span class="line"> <span class="literal">Some</span>(regex_constraints.clone()),</span><br><span class="line">))</span><br><span class="line"></span><br><span class="line">_build_query_tree(source, cursor, <span class="number">0</span>, is_cpp, <span class="literal">false</span>, <span class="literal">false</span>, regex_constraints)</span><br></pre></td></tr></table></figure></blockquote><p>QueryTree数据结构:</p><figure class="highlight rust"><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">pub</span> <span class="class"><span class="keyword">struct</span> <span class="title">QueryTree</span></span> {</span><br><span class="line"> query: Query,</span><br><span class="line"> captures: <span class="built_in">Vec</span><Capture>,</span><br><span class="line"> negations: <span class="built_in">Vec</span><NegativeQuery>,</span><br><span class="line"> variables: HashSet<<span class="built_in">String</span>>,</span><br><span class="line"> id: <span class="built_in">usize</span>,</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>转换的<code>tree_sitter query</code> (核心逻辑都在 <code>builder.rs</code> 的 <code>QueryBuilder.build</code>) </p><blockquote><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">Translate the tree below `c` into a tree-sitter query string.</span><br><span class="line">"{$func($b);_($b);}"</span><br></pre></td></tr></table></figure></blockquote><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">tree_sitter query 1: ((call_expression function:[(identifier) (field_expression) (field_identifier)] @0 arguments:(argument_list [(identifier) (field_expression) (field_identifier)] @1)) )([(identifier) (field_expression) (field_identifier)] @2 )</span><br><span class="line">tree_sitter query 0: (function_definition body: (compound_statement) @0) @1</span><br></pre></td></tr></table></figure><p>深度优先的方式递归生成query tree string,按照AST解析出来不同的节点,后面跟着的 <code>@x</code> 用来区分不同的 <code>identifier</code>,方便后面做匹配。</p><hr><p>如简单的 <code>{printf(var, bar);}</code> 生成的 <code>query-tree</code>是 : </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></pre></td><td class="code"><pre><span class="line">((call_expression </span><br><span class="line"> function: [(field_expression field: (field_identifier)@0) (identifier) @0] </span><br><span class="line"> arguments: (argument_list </span><br><span class="line"> . (identifier) @1 </span><br><span class="line"> . (identifier) @2)</span><br><span class="line"> ) </span><br><span class="line"> </span><br><span class="line"> (#eq? @0 "printf")(#eq? @1 "var")(#eq? @2 "bar")) // captures</span><br></pre></td></tr></table></figure><p>结合tree-sitter的playground来看就很容易看明白了:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/tree-sitter-playground.png" alt="tree-sitter-playground"></p><h4 id="query执行-pattern-匹配"><a href="#query执行-pattern-匹配" class="headerlink" title="query执行(pattern 匹配)"></a>query执行(pattern 匹配)</h4><p>在执行query之前会做</p><ul><li><p>对于需要正则匹配的 <code>identifer</code>做合法性确认</p><figure class="highlight rust"><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="keyword">for</span> v <span class="keyword">in</span> regex_constraints.variables() {</span><br><span class="line"> <span class="keyword">if</span> !variables.contains(v) {</span><br><span class="line"> eprintln!(<span class="string">"'{}' is not a valid query variable"</span>, v.red());</span><br><span class="line"> std::process::exit(<span class="number">1</span>)</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></li><li><p>确定待解析源码文件(<code>Verify that the --include and --exclude regexes are valid.</code>) 主要是根据后缀来</p></li></ul><p>随后就是通过管道来处理,分为:</p><ul><li>文件读取 & AST解析 <code>let (ast_tx, ast_rx) = mpsc::channel();</code></li><li>QueryTree 匹配 & 结果输出 <code>let (results_tx, results_rx) = mpsc::channel();</code></li></ul><figure class="highlight rust"><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">// Spawn worker to iterate through files, parse potential matches and forward ASTs</span></span><br><span class="line">s.spawn(<span class="keyword">move</span> |_| parse_files_worker(files, ast_tx, w, cpp));</span><br><span class="line"></span><br><span class="line"><span class="comment">// Run search queries on ASTs and apply CLI constraints</span></span><br><span class="line"><span class="comment">// on the results. For single query executions, we can</span></span><br><span class="line"><span class="comment">// directly print any remaining matches. For multi</span></span><br><span class="line"><span class="comment">// query runs we forward them to our next worker function</span></span><br><span class="line">s.spawn(<span class="keyword">move</span> |_| execute_queries_worker(ast_rx, results_tx, w, &args));</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> w.len() > <span class="number">1</span> {</span><br><span class="line"> s.spawn(<span class="keyword">move</span> |_| multi_query_worker(results_rx, w.len(), before, after));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>**这玩意描述起来就像个流水线 :D **</p><p>详细描述的话就是:在有了 <code>query-tree</code>就需要把目标文件,解析(<code>parse_files_worker</code>)成 <code>(Tree, source_code)</code>,结果发送到 <code>ast_tx</code>,然后从<code>ast_rx</code>获取这些信息来执行查询操作(<code>execute_queries_worker</code>);结果放在 <code>result_tx</code>,后面处理结果的函数会从<code>result_rx</code>获取,然后输出。</p><figure class="highlight rust"><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">parse_files_worker(files, ast_tx, w, cpp);</span><br><span class="line"> weggli::parse(....);</span><br><span class="line">execute_queries_worker(ast_rx, results_tx, w, &args); <span class="comment">// w WorkItem,里面有query-tree</span></span><br><span class="line"> qt.matches(tree.root_node(), &source);</span><br><span class="line"> match_internal(...);</span><br><span class="line"> QueryCursor.matches(...);</span><br><span class="line"> QueryTree.process_match(...);</span><br></pre></td></tr></table></figure><blockquote><p>TODO: 需要细读逻辑</p></blockquote><p>这里简单的加一句print之类的可以来看看每次query的时候目标tree是啥样的(生成过程和query tree类似)</p><figure class="highlight rust"><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">// Run query</span></span><br><span class="line"><span class="keyword">let</span> tmp_tree = tree.root_node().to_sexp();</span><br><span class="line"><span class="keyword">let</span> matches = qt.matches(tree.root_node(), &source);</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220801202121736.png" alt="image-20220801202121736"></p><p>所以这就转换成了一个字符串匹配的问题,结合之前的 <code>-R</code> ,能支持正则匹配,<strong>所以说weggli是在AST上搞正则匹配一点都没说错 :D</strong></p><h4 id="multi-query-p-参数"><a href="#multi-query-p-参数" class="headerlink" title="multi-query(-p 参数)"></a>multi-query(<code>-p</code> 参数)</h4><h5 id="漏洞模型测试"><a href="#漏洞模型测试" class="headerlink" title="漏洞模型测试"></a>漏洞模型测试</h5><p><a href="https://github.com/googleprojectzero/weggli/issues/21">Question - query construction </a> 这个issue里提到了这个场景,先还原一下场景 : </p><p><code>vuln.c</code> 是个类似的情况,尝试query</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="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><string.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">wtf</span><span class="params">(<span class="keyword">int</span> a)</span></span>{</span><br><span class="line"> <span class="keyword">return</span> a + <span class="number">1337</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">foo</span><span class="params">(<span class="keyword">int</span> bar)</span></span>{</span><br><span class="line"></span><br><span class="line"> wtf(bar);</span><br><span class="line"></span><br><span class="line"> system(bar);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">vuln</span><span class="params">(<span class="keyword">char</span> *data)</span></span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">char</span> cmd[<span class="number">2048</span>] = {<span class="number">0</span>};</span><br><span class="line"></span><br><span class="line"> <span class="built_in">sprintf</span>(cmd, <span class="string">"/bin/bash %s > /tmp"</span>, data);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> system(cmd);</span><br><span class="line">}</span><br><span class="line"></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">int</span> argc, <span class="keyword">char</span>*argv[])</span></span>{</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (argc < <span class="number">2</span>){</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> foo(<span class="number">11111</span>);</span><br><span class="line"> vuln(argv[<span class="number">1</span>]);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220803130012370.png" alt="image-20220803130012370"></p><ul><li>匹配函数定义(<code>vuln</code>)</li><li>匹配func call <code>vuln(argv[1])</code></li></ul><p>假如没有对vuln的调用,那就不回返回结果</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220803130323523.png" alt="image-20220803130323523"></p><h5 id="multi-query-实现"><a href="#multi-query-实现" class="headerlink" title="multi-query 实现"></a>multi-query 实现</h5><p>这块逻辑主要在 <code>multi_query_worker</code> ,即存在多个workitem的时候会触发,就是在匹配的时候会结合这些query,即将第一个query匹配到的结果先收集起来</p><figure class="highlight rust"><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="keyword">let</span> <span class="keyword">mut</span> query_results = <span class="built_in">Vec</span>::with_capacity(num_queries);</span><br><span class="line"><span class="keyword">for</span> _ <span class="keyword">in</span> <span class="number">0</span>..num_queries {</span><br><span class="line"> query_results.push(<span class="built_in">Vec</span>::new());</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// collect all results</span></span><br><span class="line"><span class="keyword">for</span> ctx <span class="keyword">in</span> results_rx {</span><br><span class="line"> query_results[ctx.query_index].push(ctx);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>然后根据后面的query去做过滤,找到满足的pattern就打印出来</p><figure class="highlight rust"><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">let</span> filter = |x: &<span class="keyword">mut</span> <span class="built_in">Vec</span><ResultsCtx>, y: &<span class="keyword">mut</span> <span class="built_in">Vec</span><ResultsCtx>| {</span><br><span class="line"> x.retain(|r| {</span><br><span class="line"> y.iter()</span><br><span class="line"> .any(|f| r.result.chainable(&r.source, &f.result, &f.source))</span><br><span class="line"> })</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="number">0</span>..query_results.len() {</span><br><span class="line"> <span class="keyword">let</span> (part1, part2) = query_results.split_at_mut(i + <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">let</span> a = part1.last_mut().unwrap();</span><br><span class="line"> <span class="keyword">for</span> b <span class="keyword">in</span> part2 {</span><br><span class="line"> filter(a, b);</span><br><span class="line"> filter(b, a);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="方便调试做的修改"><a href="#方便调试做的修改" class="headerlink" title="方便调试做的修改"></a>方便调试做的修改</h2><h3 id="1-打印-query-tree-和-源码-AST方便定位问题"><a href="#1-打印-query-tree-和-源码-AST方便定位问题" class="headerlink" title="1. 打印 query-tree 和 源码 AST方便定位问题"></a>1. 打印 query-tree 和 源码 AST方便定位问题</h3><p>query-tree的话增加一个 <code>-v</code> 参数就行,会把query tree打印出来</p><p><strong>少量代码测试这样是可以的</strong>,也可以使用log模块把信息打出来,不过数据太多了。</p><figure class="highlight diff"><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">diff --git a/src/main.rs b/src/main.rs</span></span><br><span class="line"><span class="comment">index a819c5c..caf9a51 100644</span></span><br><span class="line"><span class="comment">--- a/src/main.rs</span></span><br><span class="line"><span class="comment">+++ b/src/main.rs</span></span><br><span class="line"><span class="meta">@@ -468,6 +468,8 @@</span> fn execute_queries_worker(</span><br><span class="line"> .enumerate()</span><br><span class="line"> .for_each(|(i, WorkItem { qt, identifiers: _ })| {</span><br><span class="line"> // Run query</span><br><span class="line"><span class="addition">+ let tmp_tree = tree.root_node().to_sexp();</span></span><br><span class="line"><span class="addition">+ info!("AST : {}", tmp_tree);</span></span><br><span class="line"> let matches = qt.matches(tree.root_node(), &source);</span><br><span class="line"> </span><br><span class="line"> if matches.is_empty() {</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>直观多了 : </p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/image-20220802153714210.png" alt="image-20220802153714210"></p>]]></content>
<summary type="html"><h2 id="关于Weggli"><a href="#关于Weggli" class="headerlink" title="关于Weggli"></a>关于Weggli</h2><blockquote>
<p>AST Pattern Search</p>
</blockquote>
<p>核心是使用和 <code>tree-sitter</code> 库,然后搞了 <code>query-tree</code> 来在 <code>AST</code>上进行搜索,这只能说是匹配特定的代码片段,还达不到程序分析的那个级别,所以理论上只能过程内分析,而且没有上下文啥的 :D 直白点说的话,像是<code>AST</code>的正则表达式,不过某种意义上来说对于使用白盒方案快速召回一些漏洞也是一种借鉴吧。</p></summary>
<category term="工具" scheme="https://o0xmuhe.github.io/categories/%E5%B7%A5%E5%85%B7/"/>
<category term="weggli" scheme="https://o0xmuhe.github.io/tags/weggli/"/>
</entry>
<entry>
<title>MTK Preloader 踩坑</title>
<link href="https://o0xmuhe.github.io/2022/03/05/MTK-Preloader-%E8%B8%A9%E5%9D%91/"/>
<id>https://o0xmuhe.github.io/2022/03/05/MTK-Preloader-%E8%B8%A9%E5%9D%91/</id>
<published>2022-03-05T13:07:17.000Z</published>
<updated>2022-11-13T08:00:15.163Z</updated>
<content type="html"><![CDATA[<h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><ul><li>MT6737T</li><li>Android</li></ul><span id="more"></span><p>前期<code>readback</code> 什么都都正常,也切出来了各个分区,并制作了scatter。</p><p>折腾的时候发现<code>SP Flash Tool </code> 加载preloader的时候有报错:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/flash_tool_error.png" alt="flash_tool_error"></p><h3 id="看日志"><a href="#看日志" class="headerlink" title="看日志"></a>看日志</h3><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></pre></td><td class="code"><pre><span class="line">02/23/22 22:35:20.309 BROM_DLL[3848][1900]: DL_HANDLE()::Rom_Load(): ROM loaded, name = preloader (flashtool_handle_internal.cpp:4693)</span><br><span class="line"></span><br><span class="line">02/23/22 22:35:20.309 BROM_DLL[3848][1900]: DEBUG: DL_HANDLE::UpdateRomFileInfoByPreloader(): UpdateRomFileInfoByPreloader get bbchiptype : 159 (flashtool_handle_internal.cpp:4359) //chip <span class="built_in">type</span> 所以应该是强绑定的</span><br><span class="line">02/23/22 22:35:20.309 BROM_DLL[3848][1900]: DL_HANDLE()::UpdateRomFileInfoByPreloader(): Loading SV5 BL, name = preloader (flashtool_handle_internal.cpp:4374)</span><br><span class="line"></span><br><span class="line">02/23/22 22:35:20.309 BROM_DLL[3848][1900]: ERROR: DL_HANDLE(0x0BC38DE8)::UpdateRomFileInfoByPreloader(): [0]: preloader - Parse GFH_FILE_INFO error(0x00001008)! (flashtool_handle_internal.cpp:4385)</span><br><span class="line">02/23/22 22:35:20.309 BROM_DLL[3848][1900]: ERROR: DL_HANDLE(0xB7B1A8AC)::File length not match with GFH specified file length (flashtool_handle_internal.cpp:4386) // 不匹配! GFH 指定的文件长度不匹配</span><br><span class="line">02/23/22 22:35:20.309 BROM_DLL[3848][1900]: ERROR: File length (262160) / GFH specified file length (0) (flashtool_handle_internal.cpp:4387) // 这里,文件长度是xxx,但是GFH里指定的不对</span><br><span class="line">02/23/22 22:35:20.309 BROM_DLL[3848][1900]: ERROR: DL_Rom_Load(): <ERR_CHECKPOINT>[232][error][5066]</ERR_CHECKPOINT> [S_DL_PC_BL_INVALID_GFH_FILE_INFO] (flashtool_handle.cpp:941)</span><br><span class="line">02/23/22 22:35:20.309 BROM_DLL[3848][1900]: DL_Rom_Load(): DL_HANDLE->rwlock: WRITE_UNLOCK. (rwlock.cpp:476)</span><br></pre></td></tr></table></figure><p>所以:</p><ul><li>DL_HANDLE()::Rom_Load() 函数</li><li>这个平台校验了size字段和实际preloader文件的size</li></ul><h3 id="逆向分析"><a href="#逆向分析" class="headerlink" title="逆向分析"></a>逆向分析</h3><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">$ file libflashtool.v1.so</span><br><span class="line">libflashtool.v1.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, BuildID[sha1]=de4c47b0bb41c274fd42efb55ceb476bcc840d7a, not stripped</span><br></pre></td></tr></table></figure><p>在 <code>DL_HANDLE::UpdateRomFileInfoByPreloader</code> 方法中找到了校验的逻辑:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ( *((_DWORD *)var48 + <span class="number">15</span>) == <span class="number">7</span> )</span><br><span class="line">{</span><br><span class="line"> err_code = DL_HANDLE::<span class="built_in">UpdateRomFileInfoByPreloader</span>(<span class="keyword">this</span>, sys_index);<span class="comment">// updateROM File!</span></span><br><span class="line"> <span class="keyword">if</span> ( err_code )</span><br><span class="line"> <span class="keyword">return</span> err_code;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">erro_code = ROM_ID_Class::<span class="built_in">LoadGFH</span>((__int64)<span class="keyword">this</span> + <span class="number">112</span>, *((_QWORD *)ROM + <span class="number">123</span>), <span class="number">0</span>, </span><br><span class="line">(__int64)&GFH);</span><br><span class="line"><span class="keyword">if</span> ( erro_code > <span class="number">0xFFF</span> )</span><br><span class="line">{</span><br><span class="line"> r12_12 = (<span class="keyword">const</span> <span class="keyword">char</span> *)std::string::<span class="built_in">c_str</span>(ROM);</span><br><span class="line"> rbx12 = g_hBROM_DEBUG;</span><br><span class="line"> MetaTrace::<span class="built_in">MetaTrace</span>(</span><br><span class="line"> (MetaTrace *)var1030,</span><br><span class="line"> <span class="string">"FlashToolLib/source/common/handle/src/flashtool_handle_internal.cpp"</span>,</span><br><span class="line"> <span class="number">4417</span>,</span><br><span class="line"> <span class="number">0xFF</span>u,</span><br><span class="line"> <span class="string">" ERROR:"</span>);</span><br><span class="line"> MetaTrace::<span class="built_in"><span class="keyword">operator</span></span>()(</span><br><span class="line"> var1030,</span><br><span class="line"> rbx12,</span><br><span class="line"> <span class="string">"DL_HANDLE(0x%08X)::UpdateRomFileInfoByPreloader(): [%u]: %s - Load GFH_FILE_INFO error(0x%08X)! "</span>,</span><br><span class="line"> <span class="keyword">this</span>,</span><br><span class="line"> rom_file_name,</span><br><span class="line"> r12_12,</span><br><span class="line"> erro_code);</span><br><span class="line"> MetaTrace::~<span class="built_in">MetaTrace</span>((MetaTrace *)var1030);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">5066</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> ( *((_DWORD *)GFH + <span class="number">8</span>) != *((_QWORD *)ROM + <span class="number">0x7C</span>) )<span class="comment">// length check!!</span></span><br><span class="line">{</span><br><span class="line"></span><br><span class="line"> <span class="comment">//.....error log</span></span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><code>ROM + 0x7C</code> 是实际的文件大小<br><code>GFH + 8</code> 是 解析preloader 中的GFH结构中的size字段</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">ROM_ID_Class::LoadGFH</span><br><span class="line"> GFH_Find(rom_buffer, <span class="built_in">type</span>, (_QWORD *)st);</span><br><span class="line"> GFH_Internal_Parser(buff_addr, 0LL, <span class="built_in">type</span>, GFG_st);</span><br><span class="line"> </span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function">__int64 __fastcall <span class="title">GFH_Internal_Parser</span><span class="params">(__int64 buf_addr, __int64 flag_0, <span class="keyword">int</span> type, _QWORD *GFG_st)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> __int64 result; <span class="comment">// rax</span></span><br><span class="line"> __int64 st; <span class="comment">// [rsp+20h] [rbp-30h]</span></span><br><span class="line"> __int64 v8; <span class="comment">// [rsp+28h] [rbp-28h]</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> cnt; <span class="comment">// [rsp+3Ch] [rbp-14h]</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> v10; <span class="comment">// [rsp+40h] [rbp-10h]</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> i; <span class="comment">// [rsp+44h] [rbp-Ch]</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> ret; <span class="comment">// [rsp+48h] [rbp-8h]</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> reta; <span class="comment">// [rsp+48h] [rbp-8h]</span></span><br><span class="line"> <span class="keyword">char</span> v14; <span class="comment">// [rsp+4Eh] [rbp-2h]</span></span><br><span class="line"> <span class="keyword">char</span> v15_0; <span class="comment">// [rsp+4Fh] [rbp-1h]</span></span><br><span class="line"></span><br><span class="line"> v15_0 = <span class="number">0</span>;</span><br><span class="line"> v14 = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span> ( flag_0 )</span><br><span class="line"> v15_0 = <span class="number">1</span>;</span><br><span class="line"> ret = <span class="built_in">GFH_FILE_INFO_BasicCheck</span>(buf_addr);</span><br><span class="line"> <span class="keyword">if</span> ( ret > <span class="number">0xFFF</span> )</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line"> cnt = *(_DWORD *)(buf_addr + <span class="number">0x28</span>);</span><br><span class="line"> <span class="keyword">for</span> ( i = <span class="number">0</span>; i < cnt; i = v10 ) <span class="comment">// parse sub struct?</span></span><br><span class="line"> {</span><br><span class="line"> st = buf_addr + i;</span><br><span class="line"> <span class="keyword">if</span> ( (*(_DWORD *)st & <span class="number">0xFFFFFF</span>) != <span class="number">5066061</span> )</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0x1003</span>LL;</span><br><span class="line"> v10 = i + *(<span class="keyword">unsigned</span> __int16 *)(st + <span class="number">4</span>);</span><br><span class="line"> <span class="keyword">if</span> ( cnt < v10 )</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0x1005</span>LL;</span><br><span class="line"> <span class="keyword">if</span> ( v15_0 )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> ( *(_WORD *)(st + <span class="number">6</span>) <= <span class="number">0x104</span>u )</span><br><span class="line"> {</span><br><span class="line"> v8 = flag_0 + <span class="number">24LL</span> * *(<span class="keyword">unsigned</span> __int16 *)(st + <span class="number">6</span>) + <span class="number">8</span>;</span><br><span class="line"> <span class="keyword">if</span> ( *(_BYTE *)v8 )</span><br><span class="line"> {</span><br><span class="line"> reta = (*(__int64 (__fastcall **)(__int64, _QWORD))(v8 + <span class="number">8</span>))(st, *(_QWORD *)(v8 + <span class="number">16</span>));</span><br><span class="line"> <span class="keyword">if</span> ( reta > <span class="number">0xFFF</span> )</span><br><span class="line"> <span class="keyword">return</span> reta;</span><br><span class="line"> v14 = <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 class="keyword">else</span> <span class="keyword">if</span> ( type == *(<span class="keyword">unsigned</span> __int16 *)(st + <span class="number">6</span>) )</span><br><span class="line"> {</span><br><span class="line"> *GFG_st = st; <span class="comment">// [1]</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0LL</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ( v15_0 && v14 )</span><br><span class="line"> result = <span class="number">0LL</span>;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> result = <span class="number">0x1003</span>LL;</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>[1]</code> 的位置 找到这个结构,然后把指针赋值,分析这段逻辑,其实就是文件头:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/preloader_hexview.png" alt="preloader_hexview"></p><p>指定preloader文件大小是 <code>0x26794</code>,修改文件大小即可。</p><h3 id="解决报错"><a href="#解决报错" class="headerlink" title="解决报错"></a>解决报错</h3><p>所以只需要修改 preloader文件的长度为其 <code>+0x24</code> 处 4bytes代表size的字段即可<br>PS:不能修改这个长度字段</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/load_preloader_img.png" alt="load_preloader_img"></p><h2 id="深入研究"><a href="#深入研究" class="headerlink" title="深入研究"></a>深入研究</h2><blockquote><p>之前搞的平台也没注意这个问题,也没报错,但是size对不上,所以需要探究文件格式和为什么检查</p></blockquote><h3 id="什么是preloader"><a href="#什么是preloader" class="headerlink" title="什么是preloader"></a>什么是preloader</h3><ul><li><a href="https://www.cnblogs.com/wen123456/p/14034493.html">MTK6735 pre-loader源代码分析 - luoyuna - 博客园</a></li><li><a href="https://blog.csdn.net/u011784994/article/details/104898430">[MT6765]Preloader_流程分析–基于android 10_nancy的专栏-CSDN博客_android pmic</a></li><li>还有leak的MT6577的基线代码参考</li></ul><p><code>介于boot rom 和 bootloader之间的桥梁</code>,主要工作是初始化环境,包括c环境,timer,gpio,pmic,uart,i2c等以及装载lk镜像至DRAM中,建立起最基本的运行环境,最重要的就是<strong>初始化DRAM</strong>。</p><ul><li>执行在 ARM EL3</li></ul><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/preloader_birdview.png" alt="preloader_birdview"></p><h3 id="工作原理-–-启动过程"><a href="#工作原理-–-启动过程" class="headerlink" title="工作原理 – 启动过程"></a>工作原理 – 启动过程</h3><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/mtk_boot.png" alt="mtk_boot"></p><p>另一种情况是实现了ATF(Arm Trust Firmware)的时候:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/boot_with_atf.png" alt="boot_with_atf"></p><p><a href="https://blog.csdn.net/chenying126/article/details/78638944">ATF实现原理_chenying126的博客-CSDN博客_atf</a></p><ol><li>boot rom中执行boot code</li><li>把preloader加载到 ISRAM中</li><li>执行preloader,各种初始化的工作(DRAM初始化)</li><li>把bootloader(uboot, lk)加载到DRAM</li><li>跳转到lk执行</li><li>lk执行</li><li>把Linux kernel 和 ramdisk加载到DRAM</li><li>跳转到kernel</li><li>kernel执行</li><li>这是在Linux启动过程中使用的一个临时根 </li></ol><h3 id="preloader-解析"><a href="#preloader-解析" class="headerlink" title="preloader 解析"></a>preloader 解析</h3><p>preloader可以看成一个特定格式的可执行文件,所以需要找入口点。</p><p><code>/Users/muhe/Code/MTK6577/mediatek/platform/mt6577/preloader/src/init/init.s</code></p><p>github上找的一个可能是泄漏的基线代码来参考的</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></pre></td><td class="code"><pre><span class="line">.globl _start</span><br><span class="line"></span><br><span class="line">_start :</span><br><span class="line"></span><br><span class="line">b resethandler</span><br><span class="line"></span><br><span class="line">bss_start:</span><br><span class="line"></span><br><span class="line">.word _bss_start</span><br><span class="line"></span><br><span class="line">bss_end:</span><br><span class="line"></span><br><span class="line">.word _bss_end</span><br><span class="line"></span><br><span class="line">stack :</span><br><span class="line"></span><br><span class="line">.long sys_stack</span><br><span class="line"></span><br><span class="line">stacksz:</span><br><span class="line"></span><br><span class="line">.long sys_stack_sz</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line">resethandler :</span><br><span class="line"> MOV r0, #0</span><br><span class="line"> </span><br><span class="line"> MOV r1, #0</span><br><span class="line"> </span><br><span class="line"> MOV r2, #0</span><br><span class="line"> </span><br><span class="line"> MOV r3, #0</span><br><span class="line"> </span><br><span class="line"> MOV r4, #0</span><br><span class="line"> </span><br><span class="line"> MOV r5, #0</span><br><span class="line"> </span><br><span class="line"> MOV r6, #0</span><br><span class="line"> </span><br><span class="line"> MOV r7, #0</span><br><span class="line"> </span><br><span class="line"> MOV r8, #0</span><br><span class="line"> </span><br><span class="line"> MOV r9, #0</span><br><span class="line"> </span><br><span class="line"> MOV r10, #0</span><br><span class="line"> </span><br><span class="line"> MOV r11, #0</span><br><span class="line"> </span><br><span class="line"> MOV r12, #0</span><br><span class="line"> </span><br><span class="line"> MOV sp, #0</span><br><span class="line"> </span><br><span class="line"> MOV lr, #0</span><br></pre></td></tr></table></figure><p>这个特征还是很明显的,可以试试看:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/preloader_init.png" alt="preloader_init"></p><p>上面这个0xEA应该是 b指令,可以借由这个搞定基地址</p><p>然后是到<code>main.c</code>,继续人肉找特征</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/preloader_main.png" alt="preloader_main"></p><p>找到了字符串,但是没有引用关系 :( </p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/preloader_main%201.png" alt="preloader_main 1"></p><p>通过Ghrida的强制整个binary的分析,然后引用关系确定了main的位置,至此就可以往下看了,对比其他平台preloader的源码,能看个七七八八了。</p><p>PS : 基地址编译的时候可以指定的,比如在 <code>linux/bootloader/preloader/platform/mt6735/link_descriptor.ld</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></pre></td><td class="code"><pre><span class="line">OUTPUT_ARCH(arm)</span><br><span class="line"></span><br><span class="line">ENTRY(_start)</span><br><span class="line"></span><br><span class="line">romBase = <span class="number">0x00201000</span>;</span><br><span class="line">ramBase = <span class="number">0x00102180</span>;</span><br><span class="line"></span><br><span class="line">MEMORY {</span><br><span class="line"> ram : ORIGIN = ramBase, LENGTH = <span class="number">0xBA80</span></span><br><span class="line"> rom : ORIGIN = romBase, LENGTH = <span class="number">0x1F000</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>还有 : <code>linux/bootloader/preloader/platform/mt6735/default.mak</code></p><p>github真是个好地方啊,还有一个完整的MT6737平台Linux based的基线代码,全套的环境和build产物都有的,可以看到:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/mt6737_preloader_bin_list.png" alt="mt6737_preloader_bin_list"></p><ul><li>推测preloader应该是一个elf 经过copyobj之类的处理之后拼接上了特定的文件头</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">$ file *.elf</span><br><span class="line">preloader_bd6737m_35g_b_m0.elf: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, with debug_info, not stripped</span><br></pre></td></tr></table></figure><p>查看相关的makefile可以验证该猜想:</p><figure class="highlight makefile"><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="variable">$(D_BIN)</span>/preloader.elf: <span class="variable">$(D_BIN)</span>/<span class="variable">$(PL_IMAGE_NAME)</span>.elf</span><br><span class="line"></span><br><span class="line"> <span class="variable">$(OBJCOPY)</span> -R .dram <span class="variable">$(D_BIN)</span>/<span class="variable">$(PL_IMAGE_NAME)</span>.elf -O elf32-littlearm <span class="variable">$(D_BIN)</span>/preloader.elf</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">ifeq</span> (<span class="variable">$(CFG_PRELOADER_DRAM_USE)</span>, 1)</span><br><span class="line"></span><br><span class="line"><span class="section">preloader_bin: <span class="variable">$(D_BIN)</span>/<span class="variable">$(PL_DRAM_IMAGE_NAME)</span>.bin</span></span><br><span class="line"></span><br><span class="line"> <span class="variable">$(D_BIN)</span>/<span class="variable">$(PL_DRAM_IMAGE_NAME)</span>.bin: <span class="variable">$(D_BIN)</span>/<span class="variable">$(PL_IMAGE_NAME)</span>.elf</span><br><span class="line"> </span><br><span class="line"> <span class="variable">$(hide)</span> <span class="variable">$(OBJCOPY)</span> ${OBJCFLAGS} <span class="variable">$(OBJSECOND_FLAG)</span> <span class="variable">$(D_BIN)</span>/<span class="variable">$(PL_IMAGE_NAME)</span>.elf -O binary <span class="variable">$(D_BIN)</span>/<span class="variable">$(PL_DRAM_IMAGE_NAME)</span>.bin</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><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">PL_IMG_SECOND_PARTION_SECTION :=.pl_dram.text .pl_dram.data .pl_dram.rodata .pl_dram.start</span><br><span class="line">OBJSECOND_FLAG := $(addprefix -j , $(PL_IMG_SECOND_PARTION_SECTION))</span><br><span class="line"></span><br><span class="line">objcopy --gap-fill=0xff <span class="variable">$OBJSECOND_FLAG</span> input.elf -O binary output.bin</span><br></pre></td></tr></table></figure><p><code>addprefix</code>这个可以忽略</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/objcopy_test.png" alt="objcopy_test"></p><p>当然,hash想一样还是想多了,毕竟编译环境都不一样,直接上diff:</p><p>一共两处:<br><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/diff1.png" alt="diff1"></p><p>这个是多了一个GFH结构(说好的 NO_GFH 难道只是说头没有)</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/diff2.png" alt="diff2"></p><ul><li><p><code>preloader_bd6737m_35g_b_m0_LINKED.bin</code></p><ul><li> 比<code>output.bin</code> 多了一个GFH在尾部</li></ul></li><li><p><code>preloader_bd6737m_35g_b_m0_NO_GFH.bin</code></p><ul><li>中间与部分数据不一致</li><li> 比<code>output.bin</code> 多了一个GFH在尾部</li></ul></li><li><p><code>preloader_bd6737m_35g_b_m0.bin</code> 比 <code>preloader_bd6737m_35g_b_m0_NO_GFH.bin</code> 又多了一个GFH头和尾部的签名数据</p></li></ul><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/diff3.png" alt="diff3"></p><p>所以这里可以认定:</p><ul><li>preloader是一个elf,通过copyobj处理后,头、尾添加GFH相关的数据,得到MTK平台的preloader</li><li>然后MTK平台的preloader再添加EMMC BOOT头,就得到了从EMMC_BOOT_[1,2] 分区中得到的数据</li></ul><h4 id="结构"><a href="#结构" class="headerlink" title="结构"></a>结构</h4><p>这里以EMMC为例:</p><p><strong>EMMC_BOOT + GFH_INFO_EMMC + WTF1 + preloader_code + WTF2</strong></p><ul><li><p>EMMC_BOOT </p><ul><li> <code>MT6737/linux/bootloader/preloader/tools/gen-preloader-img.py</code></li></ul></li><li><p>GFH_INFO_EMMC :</p><ul><li><code>linux/bootloader/preloader/platform/mt6735/gfh/default/ns/GFH_INFO_EMMC.txt</code></li></ul></li><li><p>GFH Part 2 : GFH 的另一部分,还会修改上面的size</p><ul><li><code>linux/bootloader/preloader/tools/pbp/*</code></li></ul></li><li><p>preloader_code</p><ul><li><code>preloader.elf</code> objcopy处理之后</li></ul></li><li><p>WTF 2 : </p><ul><li>?</li></ul></li></ul><p>继续看Makefile来分析:</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><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">$(D_BIN)/$(PL_IMAGE_NAME).bin: $(D_BIN)/$(PL_IMAGE_NAME)_NO_GFH.bin $(GFH_INFO) $(GFH_HASH) $(PBP_TOOL)</span><br><span class="line"></span><br><span class="line"> @<span class="built_in">echo</span> <span class="string">"[ Only for Non-Secure Chip ]"</span></span><br><span class="line"> </span><br><span class="line"> @<span class="built_in">echo</span> <span class="string">"============================================"</span></span><br><span class="line"> </span><br><span class="line"> @<span class="built_in">echo</span> <span class="string">"INI_GFH_GEN=NO"</span></span><br><span class="line"> </span><br><span class="line"> @<span class="built_in">echo</span> <span class="string">"[ Attach <span class="subst">$(MTK_PLATFORM)</span> GFH ]"</span></span><br><span class="line"> </span><br><span class="line"> @<span class="built_in">echo</span> <span class="string">"============================================"</span></span><br><span class="line"> </span><br><span class="line"> @<span class="built_in">echo</span> <span class="string">" : GFH_INFO - <span class="subst">$(GFH_INFO)</span>"</span></span><br><span class="line"> </span><br><span class="line"> @<span class="built_in">echo</span> <span class="string">" : GFH_HASH - <span class="subst">$(GFH_HASH)</span>"</span></span><br><span class="line"> </span><br><span class="line"> cp -f $(GFH_INFO) <span class="variable">$@</span></span><br><span class="line"> </span><br><span class="line"> @chmod 777 <span class="variable">$@</span></span><br><span class="line"> </span><br><span class="line"> cat $< >> <span class="variable">$@</span></span><br><span class="line"> </span><br><span class="line"> cat $(GFH_HASH) >> <span class="variable">$@</span></span><br><span class="line"> </span><br><span class="line"> $(PBP_TOOL) <span class="variable">$@</span></span><br><span class="line"> </span><br><span class="line"> @<span class="built_in">echo</span> <span class="string">"<span class="subst">$(PBP_TOOL)</span> pass !!!!"</span></span><br><span class="line"></span><br><span class="line">// ...</span><br><span class="line"></span><br><span class="line">$(D_BIN)/$(PL_IMAGE_NAME).bin: $(D_BIN)/$(PL_IMAGE_NAME)_LINKED.bin</span><br><span class="line"></span><br><span class="line"> cp -f $< <span class="variable">$@</span></span><br></pre></td></tr></table></figure><p>遂可以得到:</p><ul><li>EMMC_BOOT<ul><li> <code>MT6737/linux/bootloader/preloader/tools/gen-preloader-img.py</code> 生成</li></ul></li><li>GFH_INFO_EMMC<ul><li> <code>**linux/bootloader/preloader/platform/mt6735/gfh/default/ns/GFH_INFO_EMMC.txt**</code> 但是这个file size字段是0xffffffff,后续会处理</li></ul></li><li>WTF1 : GFH_HASH<ul><li> GFH_HASH.txt // GFH部分会由PBP_TOOL再次处理</li></ul></li><li>preloader code<ul><li> 编译的elf经过objcopy处理之后的代码数据</li></ul></li><li> WTF2 :preloader extension</li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><p><a href="https://linux.codingbelief.com/zh/storage/flash_memory/emmc/emmc_modes.html">eMMC 工 作 模 式 · Linux Kernel Internals</a></p></li><li><p><a href="https://github.com/SoCXin/MT6737">https://github.com/SoCXin/MT6737</a></p></li><li><p><a href="https://github.com/Motorola-MT6737/android_device_motorola_mt6737-common">https://github.com/Motorola-MT6737/android_device_motorola_mt6737-common</a></p></li><li><p><a href="https://github.com/andr3jx/MTK6577.git">https://github.com/andr3jx/MTK6577.git</a></p></li><li><p><a href="https://www.cnblogs.com/wen123456/p/14034493.html">MTK6735 pre-loader源代码分析 - luoyuna - 博客园</a></p></li><li><p>[<a href="https://blog.csdn.net/u011784994/article/details/104898430">MT6765]Preloader_流程分析–基于android 10_nancy的专栏-CSDN博客_android pmic</a></p></li></ul>]]></content>
<summary type="html"><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><ul>
<li>MT6737T</li>
<li>Android</li>
</ul></summary>
<category term="其他" scheme="https://o0xmuhe.github.io/categories/%E5%85%B6%E4%BB%96/"/>
<category term="MTK" scheme="https://o0xmuhe.github.io/tags/MTK/"/>
<category term="preloader" scheme="https://o0xmuhe.github.io/tags/preloader/"/>
<category term="boot" scheme="https://o0xmuhe.github.io/tags/boot/"/>
</entry>
<entry>
<title>将Android/iOS上的流量转发到Wireshark分析</title>
<link href="https://o0xmuhe.github.io/2021/12/15/%E5%B0%86Android-iOS%E4%B8%8A%E7%9A%84%E6%B5%81%E9%87%8F%E8%BD%AC%E5%8F%91%E5%88%B0Wireshark%E5%88%86%E6%9E%90/"/>
<id>https://o0xmuhe.github.io/2021/12/15/%E5%B0%86Android-iOS%E4%B8%8A%E7%9A%84%E6%B5%81%E9%87%8F%E8%BD%AC%E5%8F%91%E5%88%B0Wireshark%E5%88%86%E6%9E%90/</id>
<published>2021-12-15T08:50:53.000Z</published>
<updated>2022-11-13T07:56:35.896Z</updated>
<content type="html"><![CDATA[<h2 id="背景-amp-amp-需求"><a href="#背景-amp-amp-需求" class="headerlink" title="背景&&需求"></a>背景&&需求</h2><p> 迫于要分析一些SDK里的协议,需要抓到所有的流量来分析交互过程,所以有了这篇记录,主要是基于<a href="https://blog.csdn.net/HorkyChen/article/details/11822657?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163955839616780265452906%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163955839616780265452906&biz_id=0&spm=1018.2226.3001.4187">实时监控Android设备网络封包</a>做的尝试,然后使用相同的思路扩展到了iOS上。</p><span id="more"></span><p>原理图:</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">tcpdump---nc---端口转发---nc----wireshark</span><br><span class="line">|------手机------||-------PC端-------|</span><br></pre></td></tr></table></figure><h2 id="Android"><a href="#Android" class="headerlink" title="Android"></a>Android</h2><blockquote><p>手机必须root</p></blockquote><ul><li>adb</li><li>tcpdump</li><li>nc </li></ul><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">tcpdump -l -n -s 0 -v -w - | nc -l -p 11233</span><br></pre></td></tr></table></figure><p>PC端:</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">adb forward tcp:11233 tcp:11233 && nc 127.0.0.1 11233 | wireshark -k -S -i -</span><br></pre></td></tr></table></figure><h2 id="iOS"><a href="#iOS" class="headerlink" title="iOS"></a>iOS</h2><blockquote><p>手机必须越狱</p></blockquote><ul><li>nc</li><li>tcpdump</li><li>iproxy(<a href="https://libimobiledevice.org/">libimobiledevice</a>)</li></ul><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">tcpdump -l -n -s 0 -v -w - | nc -l -p 11233</span><br></pre></td></tr></table></figure><p>PC端:</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">~ iproxy 11233 11233</span><br><span class="line">Creating listening port 11233 <span class="keyword">for</span> device port 11233</span><br></pre></td></tr></table></figure><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">~ nc 127.0.0.1 11233 | wireshark -k -S -i -</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://blog.csdn.net/HorkyChen/article/details/11822657?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163955839616780265452906%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163955839616780265452906&biz_id=0&spm=1018.2226.3001.4187">实时监控Android设备网络封包</a></p>]]></content>
<summary type="html"><h2 id="背景-amp-amp-需求"><a href="#背景-amp-amp-需求" class="headerlink" title="背景&amp;&amp;需求"></a>背景&amp;&amp;需求</h2><p> 迫于要分析一些SDK里的协议,需要抓到所有的流量来分析交互过程,所以有了这篇记录,主要是基于<a href="https://blog.csdn.net/HorkyChen/article/details/11822657?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163955839616780265452906%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163955839616780265452906&biz_id=0&spm=1018.2226.3001.4187">实时监控Android设备网络封包</a>做的尝试,然后使用相同的思路扩展到了iOS上。</p></summary>
<category term="环境配置踩坑" scheme="https://o0xmuhe.github.io/categories/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE%E8%B8%A9%E5%9D%91/"/>
<category term="iOS" scheme="https://o0xmuhe.github.io/tags/iOS/"/>
<category term="Android" scheme="https://o0xmuhe.github.io/tags/Android/"/>
<category term="流量分析" scheme="https://o0xmuhe.github.io/tags/%E6%B5%81%E9%87%8F%E5%88%86%E6%9E%90/"/>
<category term="Wireshark" scheme="https://o0xmuhe.github.io/tags/Wireshark/"/>
</entry>
<entry>
<title>Android Native Fuzz Demo</title>
<link href="https://o0xmuhe.github.io/2021/12/08/Android-Native-Fuzz-Demo/"/>
<id>https://o0xmuhe.github.io/2021/12/08/Android-Native-Fuzz-Demo/</id>
<published>2021-12-08T14:54:18.000Z</published>
<updated>2022-11-13T08:02:00.541Z</updated>
<content type="html"><![CDATA[<h1 id=""><a href="#" class="headerlink" title=""></a></h1><h2 id="Background"><a href="#Background" class="headerlink" title="Background"></a>Background</h2><p>TrapFuzz的思路Fuzzing Android native库,这就是个简单的Demo,只针对黑盒的库。</p><span id="more"></span><h2 id="honggfuzz-on-Android"><a href="#honggfuzz-on-Android" class="headerlink" title="honggfuzz on Android"></a>honggfuzz on Android</h2><ul><li>设置好NDK路径</li><li><code>brew install automake</code></li></ul><p>构建所有的arch(arm64-v8a, armeabi, armeabi-v7a, x86, x86_64)</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">make android-all</span><br></pre></td></tr></table></figure><p>坑1: libunwind编译的各种问题:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220824225631169.png" alt="image-20220824225631169"></p><p>macos不好使,换linux去编译,然后用ndk r20.</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220824225705237.png" alt="image-20220824225705237"></p><p>传到手机上试试看:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220824225722034.png" alt="image-20220824225722034"></p><p>然后就是写个demo,在手机上跑一下看看情况</p><p><a href="https://github.com/google/honggfuzz/issues/341">hfuzz-cc is missing on android build · Issue #341 · google/honggfuzz</a></p><p><a href="https://github.com/google/honggfuzz/issues/342">No coverage information on android · Issue #342 · google/honggfuzz</a></p><p>参数 <code>fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div,indirect-calls</code></p><p>这个参数的话会有警告信息,应该是clang 参数的问题。</p><p>后面参考了谷歌的文档,替换了参数,结果没警告了,但是cov还是0.</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><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"># muhe @ muhe-Parallels-Virtual-Platform in ~/ndk_fuzzing_demo [14:51:59] </span></span><br><span class="line">$ cat Android.mk </span><br><span class="line">LOCAL_PATH:= $(call my-dir)</span><br><span class="line"></span><br><span class="line">include $(CLEAR_VARS)</span><br><span class="line">LOCAL_PATH = .</span><br><span class="line">LOCAL_MODULE := hfuzz</span><br><span class="line">LOCAL_EXPORT_C_INCLUDES := <span class="variable">$HOME</span>/honggfuzz/includes</span><br><span class="line">LOCAL_SRC_FILES := /home/muhe/honggfuzz/libs/arm64-v8a/libhfuzz.a</span><br><span class="line">LOCAL_ARM_MODE := arm</span><br><span class="line">include $(PREBUILT_STATIC_LIBRARY)</span><br><span class="line"></span><br><span class="line">include $(CLEAR_VARS)</span><br><span class="line">LOCAL_PATH = .</span><br><span class="line">LOCAL_MODULE := hfuzzcommon</span><br><span class="line">LOCAL_EXPORT_C_INCLUDES := <span class="variable">$HOME</span>/honggfuzz/includes</span><br><span class="line">LOCAL_SRC_FILES := /home/muhe/honggfuzz/obj/<span class="built_in">local</span>/arm64-v8a/libcommon.a</span><br><span class="line">LOCAL_ARM_MODE := arm</span><br><span class="line">include $(PREBUILT_STATIC_LIBRARY)</span><br><span class="line"></span><br><span class="line">include $(CLEAR_VARS)</span><br><span class="line">LOCAL_STATIC_LIBRARIES := hfuzz hfuzzcommon</span><br><span class="line">LOCAL_SRC_FILES := fuzz_test.c</span><br><span class="line">LOCAL_MODULE := fuzz_test</span><br><span class="line">LOCAL_ARM_MODE := arm</span><br><span class="line"></span><br><span class="line">include $(BUILD_EXECUTABLE)</span><br><span class="line"></span><br><span class="line"><span class="comment"># muhe @ muhe-Parallels-Virtual-Platform in ~/ndk_fuzzing_demo [14:52:01] </span></span><br><span class="line">$ cat Application.mk </span><br><span class="line">APP_BUILD_SCRIPT := ./Android.mk</span><br><span class="line">APP_STL := c++_shared <span class="comment"># Or system, or none.</span></span><br><span class="line">APP_CFLAGS := -fsanitize=address -fno-omit-frame-pointer</span><br><span class="line">APP_LDFLAGS := -fsanitize=address </span><br><span class="line"></span><br><span class="line"><span class="comment"># muhe @ muhe-Parallels-Virtual-Platform in ~/ndk_fuzzing_demo [14:52:03] </span></span><br><span class="line">$</span><br></pre></td></tr></table></figure><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220824225744530.png" alt="image-20220824225744530"></p><hr><p>退回到<strong>honggfuzz 2.2</strong> 然后用最开始 #342 那个issue的编译参数是可以的</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220824225756287.png" alt="image-20220824225756287"></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><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"># muhe @ muhe-Parallels-Virtual-Platform in ~/ndk_fuzzing_demo [15:53:52] C:130</span></span><br><span class="line">$ cat Android.mk </span><br><span class="line">LOCAL_PATH:= $(call my-dir)</span><br><span class="line"></span><br><span class="line">include $(CLEAR_VARS)</span><br><span class="line">LOCAL_PATH = .</span><br><span class="line">LOCAL_MODULE := hfuzz</span><br><span class="line">LOCAL_EXPORT_C_INCLUDES := <span class="variable">$HOME</span>/honggfuzz/includes</span><br><span class="line">LOCAL_SRC_FILES := /home/muhe/honggfuzz/libs/arm64-v8a/libhfuzz.a</span><br><span class="line">include $(PREBUILT_STATIC_LIBRARY)</span><br><span class="line"></span><br><span class="line">include $(CLEAR_VARS)</span><br><span class="line">LOCAL_PATH = .</span><br><span class="line">LOCAL_MODULE := hfuzzcommon</span><br><span class="line">LOCAL_EXPORT_C_INCLUDES := <span class="variable">$HOME</span>/honggfuzz/includes</span><br><span class="line">LOCAL_SRC_FILES := /home/muhe/honggfuzz/obj/<span class="built_in">local</span>/arm64-v8a/libcommon.a</span><br><span class="line">include $(PREBUILT_STATIC_LIBRARY)</span><br><span class="line"></span><br><span class="line">include $(CLEAR_VARS)</span><br><span class="line">LOCAL_STATIC_LIBRARIES := hfuzz hfuzzcommon</span><br><span class="line">LOCAL_SRC_FILES := fuzz_test.c</span><br><span class="line">LOCAL_MODULE := fuzz_test</span><br><span class="line">LOCAL_CFLAGS := -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div,indirect-calls</span><br><span class="line">LOCAL_LD_FLAGS := -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div,indirect-calls</span><br><span class="line"></span><br><span class="line">include $(BUILD_EXECUTABLE)</span><br><span class="line"></span><br><span class="line"><span class="comment"># muhe @ muhe-Parallels-Virtual-Platform in ~/ndk_fuzzing_demo [15:53:53] </span></span><br><span class="line">$ cat Application.mk </span><br><span class="line">APP_BUILD_SCRIPT := ./Android.mk</span><br><span class="line"><span class="comment">#APP_STL := c++_shared # Or system, or none.</span></span><br><span class="line"><span class="comment">#APP_CFLAGS := -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div,indirect-calls </span></span><br><span class="line"><span class="comment">#APP_LDFLAGS := -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div,indirect-calls</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># muhe @ muhe-Parallels-Virtual-Platform in ~/ndk_fuzzing_demo [15:53:55] </span></span><br><span class="line">$ cat fuzz_test.c </span><br><span class="line"></span><br><span class="line"><span class="comment">#include <stdio.h></span></span><br><span class="line"><span class="comment">#include <stdlib.h></span></span><br><span class="line"><span class="comment">#include <string.h></span></span><br><span class="line"></span><br><span class="line">int test_target(char* input_file_path, char* argv_0)</span><br><span class="line">{</span><br><span class="line"> char *crash = NULL;</span><br><span class="line"> FILE *fp = fopen(input_file_path, <span class="string">"rb"</span>);</span><br><span class="line"> char c;</span><br><span class="line"> <span class="keyword">if</span> (!fp) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Error opening file\\n"</span>);</span><br><span class="line"> <span class="built_in">return</span> 0;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (fread(&c, 1, 1, fp) != 1) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Error reading file\\n"</span>);</span><br><span class="line"> fclose(fp);</span><br><span class="line"> <span class="built_in">return</span> 0;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (c != <span class="string">'t'</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Error 1\\n"</span>);</span><br><span class="line"> fclose(fp);</span><br><span class="line"> <span class="built_in">return</span> 0;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (fread(&c, 1, 1, fp) != 1) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Error reading file\\n"</span>);</span><br><span class="line"> fclose(fp);</span><br><span class="line"> <span class="built_in">return</span> 0;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (c != <span class="string">'e'</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Error 2\\n"</span>);</span><br><span class="line"> fclose(fp);</span><br><span class="line"> <span class="built_in">return</span> 0;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (fread(&c, 1, 1, fp) != 1) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Error reading file\\n"</span>);</span><br><span class="line"> fclose(fp);</span><br><span class="line"> <span class="built_in">return</span> 0;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (c != <span class="string">'s'</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Error 3\\n"</span>);</span><br><span class="line"> fclose(fp);</span><br><span class="line"> <span class="built_in">return</span> 0;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (fread(&c, 1, 1, fp) != 1) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Error reading file\\n"</span>);</span><br><span class="line"> fclose(fp);</span><br><span class="line"> <span class="built_in">return</span> 0;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (c != <span class="string">'t'</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Error 4\\n"</span>);</span><br><span class="line"> fclose(fp);</span><br><span class="line"> <span class="built_in">return</span> 0;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"!!!!!!!!!!OK!!!!!!!!!!\\n"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (fread(&c, 1, 1, fp) != 1) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Error reading file\\n"</span>);</span><br><span class="line"> fclose(fp);</span><br><span class="line"> <span class="built_in">return</span> 0;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (c == <span class="string">'1'</span>) {</span><br><span class="line"> // cause a crash</span><br><span class="line"> crash[0] = 1;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (c == <span class="string">'2'</span>) {</span><br><span class="line"> char buffer[5] = { 0 };</span><br><span class="line"> // stack-based overflow to trigger the GS cookie corruption</span><br><span class="line"> <span class="keyword">for</span> (int i = 0; i < 5; ++i)</span><br><span class="line"> strcat(buffer, argv_0);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"buffer: %s\\n"</span>, buffer);</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">"Error 5\\n"</span>);</span><br><span class="line"> }</span><br><span class="line"> fclose(fp);</span><br><span class="line"> <span class="built_in">return</span> 0;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">int main(int argc, char** argv)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span>(argc < 2) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Usage: %s <input file>\\n"</span>, argv[0]);</span><br><span class="line"> <span class="built_in">return</span> 0;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //regular single target call</span><br><span class="line"> <span class="built_in">return</span> test_target(argv[1], argv[0]);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>ndk构建命令:</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">ndk-build NDK_PROJECT_PATH=. NDK_APPLICATION_MK=Application.mk TARGET_ARCH_ABI=arm64-v8a</span><br></pre></td></tr></table></figure><h2 id="write-harness-for-so"><a href="#write-harness-for-so" class="headerlink" title="write harness for .so"></a>write harness for <code>.so</code></h2><h3 id="使用native-harness-target"><a href="#使用native-harness-target" class="headerlink" title="使用native-harness-target"></a>使用native-harness-target</h3><p>参考项目 : <a href="https://github.com/CalebFenton/native-harness-target">https://github.com/CalebFenton/native-harness-target</a></p><p>Android 7.1.1</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220824225812164.png" alt="image-20220824225812164"></p><p>可以使用这个方式跑起来,不过速度及其的慢。</p><p><strong>TODO :</strong></p><ol><li>速度问题,考虑docker Android或者qemu-kvm</li><li>port 到 Android 10</li></ol><p>Android 10 :</p><p><a href="https://github.com/rednaga/native-shim">rednaga/native-shim</a></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></pre></td><td class="code"><pre><span class="line"><span class="built_in">export</span> LD_LIBRARY_PATH=`<span class="built_in">pwd</span>`:/apex/com.android.runtime/lib::<span class="variable">$LD_LIBRARY_PATH</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">export</span> LD_LIBRARY_PATH=`<span class="built_in">pwd</span>`:/apex/com.android.runtime/lib64:<span class="variable">$LD_LIBRARY_PATH</span></span><br><span class="line">255|walleye:/data/<span class="built_in">local</span>/tmp <span class="comment"># ./shim libstr-crypt.so</span></span><br><span class="line">[*] native-shim - diff</span><br><span class="line"> [+] Attempting to load : [ libstr-crypt.so ]</span><br><span class="line"> [+] Library Loaded!</span><br><span class="line"> [+] Initializing JavaVM Instance</span><br><span class="line"> [+] Initialization success (vm=0x74eb6901c0, env=0x74eb6e06c0)</span><br><span class="line"> [+] Found JNI_OnLoad, attempting to call</span><br><span class="line"> [+] Closing library</span><br><span class="line">walleye:/data/<span class="built_in">local</span>/tmp <span class="comment">#</span></span><br></pre></td></tr></table></figure><h3 id="libpl-droidsonroids-gif-so测试"><a href="#libpl-droidsonroids-gif-so测试" class="headerlink" title="libpl_droidsonroids_gif.so测试"></a>libpl_droidsonroids_gif.so测试</h3><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220824225828132.png" alt="image-20220824225828132"></p><h2 id="work-with-honggfuzz"><a href="#work-with-honggfuzz" class="headerlink" title="work with honggfuzz"></a>work with honggfuzz</h2><blockquote><p>经典的 patch 跳转指令,实现一个debugger来获取覆盖率的方案</p></blockquote><ul><li><p>使用之前 patch跳转的方式搞覆盖率,修改honggfuzz即可</p><p>问题 : 创建JVM相关的操作耗时,影响效率</p></li><li><p>为了解决效率问题,如果可以自己写一个,初始化之后,fork,然后疯狂搞fork出来的子进程即可,这样效率就上去了。</p></li></ul><h3 id="get-all-JUMP-INS"><a href="#get-all-JUMP-INS" class="headerlink" title="get all JUMP INS"></a>get all JUMP INS</h3><p>获取patch需要patch的指令地址,直接从p0tools里抄</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> idautils</span><br><span class="line"><span class="keyword">import</span> idaapi</span><br><span class="line"><span class="keyword">import</span> ida_nalt</span><br><span class="line"><span class="keyword">import</span> idc</span><br><span class="line"></span><br><span class="line"><span class="comment"># See <https://www.hex-rays.com/products/ida/support/ida74_idapython_no_bc695_porting_guide.shtml></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> os.path <span class="keyword">import</span> expanduser</span><br><span class="line">home = expanduser(<span class="string">"~"</span>)</span><br><span class="line"></span><br><span class="line">patchpoints = <span class="built_in">set</span>()</span><br><span class="line"></span><br><span class="line">max_offset = <span class="number">0</span></span><br><span class="line"><span class="keyword">for</span> seg_ea <span class="keyword">in</span> idautils.Segments():</span><br><span class="line"> name = idc.get_segm_name(seg_ea)</span><br><span class="line"> <span class="keyword">if</span> name != <span class="string">".text"</span>:</span><br><span class="line"> <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line"> start = idc.get_segm_start(seg_ea)</span><br><span class="line"> end = idc.get_segm_end(seg_ea)</span><br><span class="line"> <span class="built_in">print</span>(<span class="built_in">hex</span>(start), <span class="built_in">hex</span>(end))</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span> func_ea <span class="keyword">in</span> idautils.Functions(start, end):</span><br><span class="line"> f = idaapi.get_func(func_ea)</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> f:</span><br><span class="line"> <span class="keyword">continue</span></span><br><span class="line"> <span class="keyword">for</span> block <span class="keyword">in</span> idaapi.FlowChart(f):</span><br><span class="line"> <span class="keyword">if</span> start <= block.start_ea < end:</span><br><span class="line"> max_offset = <span class="built_in">max</span>(max_offset, block.start_ea)</span><br><span class="line"> patchpoints.add(block.start_ea)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Warning, broken CFG?"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Round up max_offset to page size</span></span><br><span class="line">size = max_offset</span><br><span class="line">rem = size % <span class="number">0x1000</span></span><br><span class="line"><span class="keyword">if</span> rem != <span class="number">0</span>:</span><br><span class="line"> size += <span class="number">0x1000</span> - rem</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(home + <span class="string">"/Desktop/patches.txt"</span>, <span class="string">"w"</span>) <span class="keyword">as</span> f:</span><br><span class="line"> f.write(ida_nalt.get_root_filename() + <span class="string">':'</span> + <span class="built_in">hex</span>(size) + <span class="string">'\\n'</span>)</span><br><span class="line"> f.write(<span class="string">'\\n'</span>.join(<span class="built_in">map</span>(<span class="built_in">hex</span>, <span class="built_in">sorted</span>(patchpoints))))</span><br><span class="line"> f.write(<span class="string">'\\n'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"Done, found {} patchpoints"</span>.<span class="built_in">format</span>(<span class="built_in">len</span>(patchpoints)))</span><br></pre></td></tr></table></figure><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220824230049472.png" alt="image-20220824230049472"></p><h3 id="Patch-or-hook-INS"><a href="#Patch-or-hook-INS" class="headerlink" title="Patch or hook INS"></a>Patch or hook INS</h3><p>问题 : 需要想办法做到 hfuzzcc一样的效果,即 把 libhfuzz.a 链接进目标binary,不然没有桩信息。</p><ul><li>看看hfuzzcc是怎么工作的</li></ul><p>看起来就是一层wrapper,给clang/gcc编译的时候增加了 <code>CFLAGS</code> 和 <code>LDFLAGS</code>,看起来只需要按照需求把参数放到 <code>Android.mk</code>即可。</p><p>这里参考ImageIO例子中的编译的参数</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><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">cc</span><br><span class="line">-I/Users/vuln_test/honggfuzz/includes/</span><br><span class="line">-Wno-unused-command-line-argument</span><br><span class="line">-fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div,indirect-calls</span><br><span class="line">-mllvm</span><br><span class="line">-sanitizer-coverage-prune-blocks=1</span><br><span class="line">-fno-inline</span><br><span class="line">-fno-builtin</span><br><span class="line">-fno-omit-frame-pointer</span><br><span class="line">-D__NO_STRING_INLINES</span><br><span class="line"></span><br><span class="line">-DHFND_FUZZING_ENTRY_FUNCTION_CXX(x,y)=extern const char* LIBHFNETDRIVER_module_netdriver;const char** LIBHFNETDRIVER_tmp1 = &LIBHFNETDRIVER_module_netdriver;extern <span class="string">"C"</span> int HonggfuzzNetDriver_main(x,y);int HonggfuzzNetDriver_main(x,y)</span><br><span class="line"></span><br><span class="line">-DHFND_FUZZING_ENTRY_FUNCTION(x,y)=extern const char* LIBHFNETDRIVER_module_netdriver;const char** LIBHFNETDRIVER_tmp1 = &LIBHFNETDRIVER_module_netdriver;int HonggfuzzNetDriver_main(x,y);int HonggfuzzNetDriver_main(x,y)</span><br><span class="line"></span><br><span class="line">-Wl,-U,_HonggfuzzNetDriver_main</span><br><span class="line">-Wl,-U,_LIBHFUZZ_module_instrument</span><br><span class="line">-Wl,-U,_LIBHFUZZ_module_memorycmp</span><br><span class="line">**-o</span><br><span class="line">runner**</span><br><span class="line">**runner.m**</span><br><span class="line">-framework</span><br><span class="line">Foundation</span><br><span class="line">-framework</span><br><span class="line">CoreGraphics</span><br><span class="line">-framework</span><br><span class="line">AppKit</span><br><span class="line">/tmp/libhfnetdriver.501.7140081f7cd58e92.a</span><br><span class="line">/tmp/**libhfuzz**.501.2fdc27091cd8b54d.a</span><br><span class="line">/tmp/libhfuzz.501.a5556386f906dc80.a</span><br><span class="line">-pthread</span><br><span class="line">-ldl</span><br><span class="line">include $(CLEAR_VARS)</span><br><span class="line">LOCAL_STATIC_LIBRARIES := hfuzz hfuzzcommon </span><br><span class="line">LOCAL_SRC_FILES := fuzz_test.c</span><br><span class="line">LOCAL_MODULE := fuzz_test</span><br><span class="line">LOCAL_CFLAGS := -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div,indirect-calls \\</span><br><span class="line"> -fno-omit-frame-pointer -fno-inline -fno-builtin \\</span><br><span class="line"> -fno-omit-frame-pointer -Wl,-u,_LIBHFUZZ_module_instrument -Wl,-u,_LIBHFUZZ_module_memorycmp -ldl</span><br><span class="line"></span><br><span class="line">LOCAL_LDFLAGS := -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div,indirect-calls \\</span><br><span class="line"> -fno-inline -fno-builtin \\</span><br><span class="line"> -fno-omit-frame-pointer -Wl,-u,_LIBHFUZZ_module_instrument -Wl,-u,_LIBHFUZZ_module_memorycm</span><br></pre></td></tr></table></figure><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220824230115528.png" alt="image-20220824230115528"></p><p>harness里需要主动调用 <code>initializeTrapfuzz()</code></p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220824230125768.png" alt="image-20220824230125768"></p><p>看起来一切都不错!</p><p>获取指定so地址也有了:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220824230138101.png" alt="image-20220824230138101"></p><ul><li>Patch</li></ul><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220824230152707.png" alt="image-20220824230152707"></p><p>参考这里修改honggfuzz的代码即可</p><h2 id="后续-amp-问题"><a href="#后续-amp-问题" class="headerlink" title="后续&问题"></a>后续&问题</h2><p>这个方案主要是效率实在是太差了,性能损耗都在jvm获取那里了,本来也是工作之余的一个小点子,后面也没深入去看了,个人最开始的想法是 winafl模式搬到安卓上 lol…</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://googleprojectzero.blogspot.com/2020/04/fuzzing-imageio.html">https://googleprojectzero.blogspot.com/2020/04/fuzzing-imageio.html</a></p><p><a href="https://github.com/googleprojectzero/p0tools/blob/master/TrapFuzz/trapfuzz.patch">https://github.com/googleprojectzero/p0tools/blob/master/TrapFuzz/trapfuzz.patch</a></p>]]></content>
<summary type="html"><h1 id=""><a href="#" class="headerlink" title=""></a></h1><h2 id="Background"><a href="#Background" class="headerlink" title="Background"></a>Background</h2><p>TrapFuzz的思路Fuzzing Android native库,这就是个简单的Demo,只针对黑盒的库。</p></summary>
<category term="Android" scheme="https://o0xmuhe.github.io/categories/Android/"/>
<category term="Android" scheme="https://o0xmuhe.github.io/tags/Android/"/>
<category term="native" scheme="https://o0xmuhe.github.io/tags/native/"/>
<category term="jni" scheme="https://o0xmuhe.github.io/tags/jni/"/>
<category term="honggfuzz" scheme="https://o0xmuhe.github.io/tags/honggfuzz/"/>
<category term="trapfuzz" scheme="https://o0xmuhe.github.io/tags/trapfuzz/"/>
</entry>
<entry>
<title>ByteCTF2021 chatroom writeup</title>
<link href="https://o0xmuhe.github.io/2021/10/17/ByteCTF2021-chatroom-writeup/"/>
<id>https://o0xmuhe.github.io/2021/10/17/ByteCTF2021-chatroom-writeup/</id>
<published>2021-10-17T10:10:17.000Z</published>
<updated>2022-11-13T08:01:36.786Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p> 在今年的ByteCTF中,我出了一道pwn题目,距离上一次打比赛/出题已经过去很久了,所以传统的 <code>heap trick</code> 就没有考虑,而是从我日常工作中挖掘的安全风险入手,简化场景,出了一道 <code>chatroom</code> ,看起来像一个web的奇怪题目。</p> <span id="more"></span><p> 这个题目其实背后是 <code>Headless Chrome</code> 相关的pwn,我早期的一篇博客其实已经阐述过相关风险,可以参考 <a href="https://o0xmuhe.github.io/2021/05/26/Chrome-headless-exploit/">Exploit Headless Chrome</a>。 其实这个风险暴露出来的不仅仅是:低版本、误用参数 这两个显而易见的问题,其背后的原因是一些不好的编程习惯被错误地传播:大家都在用 <code>--no-sandbox</code> 参数,好像 <code>it works</code>就够了,但是在实际场景中,这是很危险的。</p><h2 id="题目设计思路"><a href="#题目设计思路" class="headerlink" title="题目设计思路"></a>题目设计思路</h2><p> 我的本意是设计一个类似聊天室的场景,用户可以在聊天室内发送消息、多媒体文件、链接等,尽可能模拟一个真实场景。 处于风控考虑,对于非白名单的链接,需要进行检查(是否恶意,色流等)。对于URL 检查的逻辑,最好是服务端接收到内容之后,判断是否是URL,随后通过RPC调用走到URL检查的服务去。但是考虑到实际题目,我大大简化了这个场景,直接把检查放在前端了,而且我没有混淆JS,所以可以很直接看到一个HTTP请求。</p><p> 解决了场景问题,聊天室部分直接用了github的开源项目 <a href="https://github.com/cleverqin/node-websocket-Chatroom">node-websocket-Chatroom</a>,后端使用 puppeteer来抓取用户的URL。</p><p>为了提升一些难度,同时这也是我曾经遇到过的问题:UA不可靠的情况下怎么判断Chrome版本?</p><p>所以我直接在启动参数里把UA给改了:</p><figure class="highlight javascript"><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><br><span class="line"> <span class="keyword">const</span> browser = <span class="keyword">await</span> puppeteer.launch({ </span><br><span class="line"> <span class="attr">args</span>: [<span class="string">'--no-sandbox'</span>, <span class="string">'--disable-setuid-sandbox'</span>, </span><br><span class="line"> <span class="string">'--user-agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)</span></span><br><span class="line"><span class="string"> AppleWebKit/537.36 (KHTML, like Gecko) Chrome/1337.13.37.0 Safari/4141.42"'</span>], </span><br><span class="line"><span class="attr">ignoreHTTPSErrors</span>: <span class="literal">true</span>, <span class="attr">dumpio</span>: <span class="literal">false</span> });</span><br></pre></td></tr></table></figure><p>最终题目成型:</p><ul><li>非最新版本puppeteer</li><li>–no-sandbox</li><li>UA不准确</li></ul><h2 id="Writeup-amp-非预期"><a href="#Writeup-amp-非预期" class="headerlink" title="Writeup & 非预期"></a>Writeup & 非预期</h2><h3 id="非预期"><a href="#非预期" class="headerlink" title="非预期"></a>非预期</h3><p>主要是 <a href="https://twitter.com/zh1x1an2">zh1x1an2</a> 同学的做法,Exploit狂轰滥炸术,挨个挨个来,最终拿到flag。</p><h3 id="预期"><a href="#预期" class="headerlink" title="预期"></a>预期</h3><p>UA不可信,但是V8 和 Blink是可信的,不同Chrome版本会有不同的features,所以可以借助这个点,判断一个大版本,便于后续做利用。</p><p>参考 : <a href="https://chromestatus.com/features">https://chromestatus.com/features</a> </p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/chrome_features.png" alt="chrome_features"></p><p>不过这个需要一些积累 && 测试 :)</p><p>随后判断出来版本是 M88 之后,找个合适的nday就可以打了 : )</p><h2 id="题目环境"><a href="#题目环境" class="headerlink" title="题目环境"></a>题目环境</h2><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">docker pull muhe/ctf_chal_chatroom:v7</span><br><span class="line"></span><br><span class="line">docker run -dit --name chatroom1 -p 3000:3000 -p 31337:31337 muhe/ctf_chal_chatroom:v7</span><br></pre></td></tr></table></figure><p>访问 <code>http://localhost:3000</code> 就可以本地测试题目了 :)</p><h2 id="不足之处"><a href="#不足之处" class="headerlink" title="不足之处"></a>不足之处</h2><ol><li>无法防止爆破这种非预期解题方式。</li><li>使用nday似乎很无趣,但是塞进去一个洞,给一个 <code>patch.diff</code> 似乎又有点奇怪,偏离题目原本的出发点。</li></ol>]]></content>
<summary type="html"><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p> 在今年的ByteCTF中,我出了一道pwn题目,距离上一次打比赛/出题已经过去很久了,所以传统的 <code>heap trick</code> 就没有考虑,而是从我日常工作中挖掘的安全风险入手,简化场景,出了一道 <code>chatroom</code> ,看起来像一个web的奇怪题目。</p></summary>
<category term="Browser" scheme="https://o0xmuhe.github.io/categories/Browser/"/>
<category term="writeup" scheme="https://o0xmuhe.github.io/tags/writeup/"/>
<category term="pwn" scheme="https://o0xmuhe.github.io/tags/pwn/"/>
<category term="CTF" scheme="https://o0xmuhe.github.io/tags/CTF/"/>
</entry>
<entry>
<title>Expand Chrome Exploit : From client to server</title>
<link href="https://o0xmuhe.github.io/2021/10/04/Expand-Chrome-From-client-to-server/"/>
<id>https://o0xmuhe.github.io/2021/10/04/Expand-Chrome-From-client-to-server/</id>
<published>2021-10-03T16:30:35.000Z</published>
<updated>2023-11-23T08:40:50.879Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p> 过去的近一年的时间(本文在21年开的头,期间一直是hidden状态),我接触了<del>万恶的</del>浏览器安全,不过只是一个脚本小子的水平 :( </p><span id="more"></span><p> 最开始是由于一些工作上的因素,关注了一些主流的IM客户端,难易程度不等,当然也看了不少前辈的精彩工作,比如二哥的各种奇妙的xss、伪协议打🐧啥的。无奈功夫不到家只能另辟蹊径,再加上大学时候@wuyan学长某次回学校给我们做小组分享的时候展示了当时印象笔记的一个xss的时候提了一句,很多客户端你可以把它当成一个浏览器来看;至此这才有了后来的探究和一点点成果吧,时至今日,相关漏洞早已修复,攻击手法也早已“众所周知”,所以写个记录也没有什么:D </p><h2 id="客户端"><a href="#客户端" class="headerlink" title="客户端"></a>客户端</h2><blockquote><p>主要是一些Electron和CEF客户端</p></blockquote><h3 id="一些背景-amp-调研工作"><a href="#一些背景-amp-调研工作" class="headerlink" title="一些背景 & 调研工作"></a>一些背景 & 调研工作</h3><blockquote><p>主流客户端的情况,以前 & 现在</p></blockquote><p> 关注客户端安全的同学应该会发现Electron&CEF的应用越来越广泛了,从早些的时候某音乐播放器的xss2rce到后面被关注到内置浏览器本身,当然大佬可能更早的时候就这么玩了 :D </p><p>其实是用浏览器框架来开发客户端是一直以来就有的东西,比如下面这张图(可能不完全): </p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/t013239e0facc311d64.png"></p><blockquote><p>也有直接从chromium做定制开发的,即原生的方式开发,比如某先进IM</p></blockquote><p>这么做的好处是显而易见的:</p><ul><li><p>使用成熟的嵌入式浏览器框架(cef, electron等)能够快速开发应用</p></li><li><p>能够规避很多复杂的底层设计(c/c++)</p></li><li><p>前端–>APP跨平台的特性,且很灵活,</p></li><li><p>更加方便支持自定义协议/扩展/JS对象等</p></li><li><p>…</p><p>与此同时,浏览器的攻击面就自然而然地引入进来了,再结合客户端本身,1+1>2的即视感。本文重点关注浏览器相关的内容,那按照浏览器的思路去考虑就是:</p></li></ul><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">攻击者构造恶意页面-->客户端访问-->RCE</span><br><span class="line"> |</span><br><span class="line"> Render---(sbx)--->Broker</span><br></pre></td></tr></table></figure><p>这条攻击链路上的前置条件是**客户端可以打开任意URL(直接or间接)**,随后就是常规的浏览器Exploit,分成Render RCE+SBX两部分。</p><p>妙就妙在很多客户端出于一些特殊的需求他没有开沙箱。</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/images.jpeg" alt="妙啊"></p><p>这里有一份统计 : <a href="https://github.com/sickcodes/no-sandbox">https://github.com/sickcodes/no-sandbox</a>,当然也不一定对(比如微信是CEF吧),有些客户端也发生了一些变化,不过可以通过历史记录看出来变化趋势,大家都在慢慢地开启沙箱,尝试逐渐收敛风险。</p><p> <code>--no-sandbox</code>的风险是显而易见的,另一个问题是<code>patch gap</code>,chromium那个更新频率没有几个客户端能跟上,甚至说基本跟不上,再加上功能优先,版本升级或者补丁合入并没有那么高的优先级(也有可能是风险没体现出来,不受重视),所以大部分基于chromium的客户端多多少少都滞后一些大版本,这就造成了大量潜在的Nday影响这些客户端,甚至从RCE到SBX一条龙。</p><h3 id="攻击思路"><a href="#攻击思路" class="headerlink" title="攻击思路"></a>攻击思路</h3><h4 id="是否直接打开URL"><a href="#是否直接打开URL" class="headerlink" title="是否直接打开URL"></a>是否直接打开URL</h4><ul><li>直接打开,发链接点了就内置浏览器打开,这类早期IM会这样干,可能是为了“用户体验没有割裂感”</li><li>特殊的消息才会内置浏览器打开,比如卡片?</li><li>是否有url白名单,不能绕过的话可能需要多一个白名单域名下的xss做桥梁</li><li>特殊的scheme有url参数,参考Android客户端的那种情况</li><li>其他的奇奇怪怪的入口,比如监听端口,处理函数有个啥<code>openBrowser</code>的东西</li></ul><h4 id="Chromium版本确定"><a href="#Chromium版本确定" class="headerlink" title="Chromium版本确定"></a>Chromium版本确定</h4><ul><li>UA,这个不一定准吧,毕竟启动参数是可以改的,代码里也可以改,一般情况下是可信的</li><li>JS引擎特性,这个我在之前一个文章里提到过<a href="https://o0xmuhe.github.io/2021/10/17/ByteCTF2021-chatroom-writeup/">ByteCTF2021 chatroom writeup</a>,用于探测后端puppeteer的版本</li></ul><p>以上两种方式结合是最好的,能判断出来很精准的版本</p><h4 id="选个好-quot-day-quot"><a href="#选个好-quot-day-quot" class="headerlink" title="选个好"day""></a>选个好<code>"day"</code></h4><ul><li><p>Pj0/github/twitter/v8 commits</p></li><li><p>@BugsChromium </p></li></ul><p>版本-代码commit之前互查询可以参考: <a href="https://omahaproxy.appspot.com/">https://omahaproxy.appspot.com/</a></p><h3 id="case1"><a href="#case1" class="headerlink" title="case1"></a>case1</h3><blockquote><p><code>xxminibrowser</code> (xx是啥我也不知道)这个洞应该在21年上半年就修复了,而且陆陆续续补掉了不少攻击的前置条件</p></blockquote><ul><li>Open URL</li></ul><p>最早可以随便打开,后面就变成了特殊的消息,再后来越来越窄吧</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220824215023030.png" alt="image-20220824215023030"></p><ul><li>版本确定</li></ul><p>用上面的方法确定了具体的版本,还是很准确的</p><ul><li><p>选个好day</p><p>当然了,在那个时候这个客户端他的好兄弟“小而美”也是差不多的情况,好好选day能都打了,21年hvv爆出的RCE就是这个情况(藏洞没有好下场 - -!)</p></li></ul><p> 当时用的是crbug659475,挺好用的,感谢keen lab的大哥 :D 为了提高成功率甚至还做了这样的事情:</p><figure class="highlight javascript"><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="keyword">var</span> worker;</span><br><span class="line"><span class="keyword">var</span> exploitSucc = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">startExploit</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">if</span>(exploitSucc){</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> worker = <span class="keyword">new</span> Worker(<span class="string">'exp.js'</span>);</span><br><span class="line"> </span><br><span class="line"> worker.onmessage = <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>{</span><br><span class="line"> exploitSucc = e.data;</span><br><span class="line"> <span class="keyword">if</span> (exploitSucc == <span class="literal">false</span>) {</span><br><span class="line"> <span class="built_in">document</span>.write(<span class="string">"exploit failed, retry....<hr>"</span>);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">document</span>.write(<span class="string">"exploit done!!!!!<hr>"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">startExploit();</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> hangMonitor = <span class="built_in">setInterval</span>(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (exploitSucc == <span class="literal">true</span>) {</span><br><span class="line"> <span class="built_in">clearInterval</span>(hangMonitor);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> startExploit();</span><br><span class="line"> }</span><br><span class="line">}, <span class="number">20000</span>);</span><br></pre></td></tr></table></figure><hr><p>2022.8 update</p><p>“小而美”好像在hvv期间开了 <code>--jit-less</code>后面又下掉了 ,现在的cmdline,与此同时也升级到了M81</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/wechat_browser_render_cmdline.png" alt="wechat"></p><h3 id="case2"><a href="#case2" class="headerlink" title="case2"></a>case2</h3><blockquote><p>S***e</p></blockquote><ul><li>Open URL</li></ul><p>这个点说来还有点故事,20年的时候发现了,直到21年吧有一个老外也发现了并且发在了推特上</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220824215815171.png" alt="image-20220824215815171"></p><p>说来也简单,就是个看起来是A打开确实B的问题,主要服务端也不做校验就转发是有点离谱的; 对于打开的URL也是有白名单检查的,所以特定域下的xss是攻击的桥梁 :(</p><ul><li>版本</li></ul><p>M78 这个没什么好说的 </p><ul><li>选个day</li></ul><p>m78可选的很多(比如CVE-2020-6418),注意目标的是x86,需要做一些改造,而且之前遇到过有些洞只在x86_64 work的情况</p><hr><p>2022.8 update</p><p>参数 & 版本</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/note_pic/skype_render_cmdline.png" alt="skype"></p><h3 id="case3"><a href="#case3" class="headerlink" title="case3"></a>case3</h3><blockquote><p>Android webview</p></blockquote><p> 这类其实也算个重灾区,很多厂商会选择自己定制webview,且为了方便不开沙箱,线上丰富多彩的功能也提供了很多攻击的入口,发链接、扫码、卡片消息;但是也都会在打开URL前考虑加一层拦截,提示用户“xxx不是xxxxxx,确定要打开吗”。但是21年反垄断之后,这个限制就下掉了,随之而来的就是这样的安全风险。</p><p> 对于甲方来说就是,我的定制webview依赖chromium,我又没办法及时更新,沙箱也一定能开,在 nday和 <code>patch gap</code>的双重打击之下,你的SRC可能就变成“提款机”,每个月谷歌一发补丁,再加上是不是爆出个在野利用,你的SRC一定经常收到这样的报告: <code>xxxxx RCE </code>。</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/3c70b0f75f2f4b6184bb461e41adc428.jpeg" alt="sad"></p><p>我也有做安全运营的朋友<del>饱受其害</del>,我只能建议他内部专项治理,定期合补丁,能上沙箱就沙箱,这个真没啥好办法。</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><h4 id="作为攻击方"><a href="#作为攻击方" class="headerlink" title="作为攻击方"></a>作为攻击方</h4><ul><li>搞定入口,这个比较吃经验了,见招拆招吧</li><li>多盯着点commit,开发一些工具啥的也可以,方便用</li><li>利用武器化,不是只弹个计算器就完了的</li></ul><h4 id="作为防守方"><a href="#作为防守方" class="headerlink" title="作为防守方"></a>作为防守方</h4><ul><li><p>打补丁case by case,但是每个月都要来那么一次,还不能全自动化,有效但费人力。</p></li><li><p>开沙箱,毕竟是个浏览器,还是能打IPC穿沙箱穿出来,不过这就要看具体漏洞情况了。</p></li><li><p>升级到最新版,如果不稳定怎么办,这个也不是个好办法</p></li></ul><p><code>补丁+升级+沙箱</code> 三个维度一起来,毕竟短板效应,少了哪一块都不行,甲方的话也可以搞一些白盒工具来做补丁check,确认漏洞是否存在,这块就见仁见智了,我也写过一套,效果还行:D </p><h2 id="服务端"><a href="#服务端" class="headerlink" title="服务端"></a>服务端</h2><blockquote><p>chrome headless_shell 和 puppeteer</p></blockquote><p>基本上还是 <a href="https://o0xmuhe.github.io/2021/05/26/Chrome-headless-exploit/">Exploit Headless Chrome</a>这篇文章的内容,核心问题还是沙箱&版本过低的问题,这块比较严重的是网上很多人写教程、博客都喜欢<code>--no-sandbox</code>,我也不知道他们知不知道这个参数的影响,不过一传十十传百,你会发现很多后端无头浏览器多多少少有这类问题。</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/ae12bb08f457409763f629da49aabc8f.jpg" alt="no"></p><h3 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h3><h4 id="作为防守方-1"><a href="#作为防守方-1" class="headerlink" title="作为防守方"></a>作为防守方</h4><ul><li>安全开发意识提高,不要为了方便乱用参数</li><li>及时更新版本or打补丁</li></ul><p><del>扯远一些,有些扫描器用chrome,可以使用这手段做反制,你敢爬我轻则crash重则rce。</del></p><h4 id="作为攻击方-1"><a href="#作为攻击方-1" class="headerlink" title="作为攻击方"></a>作为攻击方</h4><ul><li>版本探测比较重要,做这个操作前先想想银手镯</li><li>什么?你还想exp?我看你想戴上银手镯</li></ul><h2 id="为了风险治理做了什么"><a href="#为了风险治理做了什么" class="headerlink" title="为了风险治理做了什么"></a>为了风险治理做了什么</h2><blockquote><p>说到这个就想到了2020.11.13 那个下午弹出计算器的时候</p></blockquote><p> 主要是三块吧,我发现甲方里涮一圈之后思维确实不太一样了。</p><ul><li><p>首先要讲明白风险,这里也包含证明风险,需要强有力的证明,比如exp打穿这样,研发可能不太理解为什么这样可以RCE,这就需要沟通好让大家有相同的sense</p></li><li><p>其次是修复方案,不同业务线、场景不一样,这个得和业务聊明白了才好给方案,不然就是“空中楼阁”,这块就算是治理存量问题了</p><ul><li>沙箱开不开</li><li>补丁无法自动合入,怎么处理更高效,能不能自动化节省排期</li><li>以后怎么办,建立个什么流程跑这个事情</li></ul></li><li><p>最后可以开发一些工具做一些预警工作,相当于治理增量问题</p><ul><li><p>存量怎么扫,补丁怎么提取,这个部分得好好设计构思</p></li><li><p>预警Bot,这个本质就是个爬虫+机器人,之前研究的时候自己搞过一个tg bot专门干这个,还能搞漏洞查询 </p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/image-20220824224612405.png" alt="image-20220824224612405" style="zoom:50%;" /></li></ul></li></ul><h2 id="总结-2"><a href="#总结-2" class="headerlink" title="总结"></a>总结</h2><p> 我认为很有意思的是这个攻击面对于防守方来说简直是“折磨”,只要你的项目使用chromium,你就不得不面临各种补丁、升级,这实际上是很难做到及时补丁&升级的,所以理论上存在<code>patch gap</code>,这就导致很多吸引眼球的 <code>xxx RCE</code> 传播的非常广泛。早在21年7月份,腾讯的蓝军在21年发布了<a href="https://cloud.tencent.com/developer/article/1848763">攻防启示:Chromium组件风险剖析与收敛</a>,也详细地剖析了该攻击面以及修复方案,对于我自己来说比较可惜的是在公司内部搞了这块攻击面的治理工作没有出去讲一讲or发个文章啥的,到后面这篇文章出来后已经没什么可讲的了 :( </p><p> 主要想对自己的一些工作做个简单的总结,所以才有了本文,时至2022.8,这个攻击面应该已经变得众所周知,没有什么秘密可言了,想来这手法我在17年某项目上也见过,不过当时是webkit。</p>]]></content>
<summary type="html"><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p> 过去的近一年的时间(本文在21年开的头,期间一直是hidden状态),我接触了<del>万恶的</del>浏览器安全,不过只是一个脚本小子的水平 :( </p></summary>
<category term="Browser" scheme="https://o0xmuhe.github.io/categories/Browser/"/>
<category term="Chrome" scheme="https://o0xmuhe.github.io/tags/Chrome/"/>
<category term="Exploit" scheme="https://o0xmuhe.github.io/tags/Exploit/"/>
</entry>
<entry>
<title>iOS RE 4 beginners 3 - fishhook</title>
<link href="https://o0xmuhe.github.io/2021/07/24/iOS-RE-4-beginners-3-fishhook/"/>
<id>https://o0xmuhe.github.io/2021/07/24/iOS-RE-4-beginners-3-fishhook/</id>
<published>2021-07-23T16:05:49.000Z</published>
<updated>2022-11-13T07:58:04.824Z</updated>
<content type="html"><![CDATA[<h1 id="关于"><a href="#关于" class="headerlink" title="关于"></a>关于</h1><p>Fishhook是Facebook提供的利用MachO文件惰性加载原理,通过修改懒加载和非懒加载两个表的指针达到C函数HOOK的目的一个轻量级的hook库。理解这个工具和熟悉流程也是可以帮助更好的理解MachO文件格式 :)</p><span id="more"></span><p>原理图如下:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/fishhook.png" alt="fishhook"></p><h1 id="源码阅读"><a href="#源码阅读" class="headerlink" title="源码阅读"></a>源码阅读</h1><p>核心其实就是rebind_symbols 这个接口,另一个 <code>rebind_symbols_image</code> 是指定macho中的symbol进行rebind,所以从 <code>rebind_symbols</code>函数看起就行了。</p><figure class="highlight c"><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="function">FISHHOOK_VISIBILITY</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">rebind_symbols</span><span class="params">(struct rebinding rebindings[], <span class="keyword">size_t</span> rebindings_nel)</span></span>;</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></pre></td><td class="code"><pre><span class="line">rebind_symbols(struct rebinding rebindings[], <span class="keyword">size_t</span> rebindings_nel);</span><br><span class="line"> _rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i));</span><br><span class="line"> rebind_symbols_for_image(_rebindings_head, header, slide);</span><br><span class="line"> perform_rebinding_with_section(...)</span><br></pre></td></tr></table></figure><p><code>_rebindings_head</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><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">rebinding</span> {</span></span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">char</span> *name;</span><br><span class="line"> <span class="keyword">void</span> *replacement;</span><br><span class="line"> <span class="keyword">void</span> **replaced;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">rebindings_entry</span> {</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">rebinding</span> *<span class="title">rebindings</span>;</span></span><br><span class="line"> <span class="keyword">size_t</span> rebindings_nel;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">rebindings_entry</span> *<span class="title">next</span>;</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">rebindings_entry</span> *_<span class="title">rebindings_head</span>;</span></span><br><span class="line"><span class="keyword">segment_command_t</span> *cur_seg_cmd;</span><br><span class="line"> <span class="keyword">segment_command_t</span> *linkedit_segment = <span class="literal">NULL</span>;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">symtab_command</span>* <span class="title">symtab_cmd</span> =</span> <span class="literal">NULL</span>;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">dysymtab_command</span>* <span class="title">dysymtab_cmd</span> =</span> <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">uintptr_t</span> cur = (<span class="keyword">uintptr_t</span>)header + <span class="keyword">sizeof</span>(<span class="keyword">mach_header_t</span>); <span class="comment">// now, cur points to LOAD_CMDs</span></span><br><span class="line"> <span class="comment">// iter LOAD CMDs</span></span><br><span class="line"> <span class="keyword">for</span> (uint i = <span class="number">0</span>; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {</span><br><span class="line"> cur_seg_cmd = (<span class="keyword">segment_command_t</span> *)cur;</span><br><span class="line"> <span class="comment">// find LINK_EDIT seg</span></span><br><span class="line"> <span class="keyword">if</span> (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(cur_seg_cmd->segname, SEG_LINKEDIT) == <span class="number">0</span>) {</span><br><span class="line"> linkedit_segment = cur_seg_cmd;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (cur_seg_cmd->cmd == LC_SYMTAB) {</span><br><span class="line"> <span class="comment">// find SYMTAB CMD</span></span><br><span class="line"> symtab_cmd = (struct symtab_command*)cur_seg_cmd;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (cur_seg_cmd->cmd == LC_DYSYMTAB) {</span><br><span class="line"> <span class="comment">// find DYSYM CMD</span></span><br><span class="line"> dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!symtab_cmd || !dysymtab_cmd || !linkedit_segment ||</span><br><span class="line"> !dysymtab_cmd->nindirectsyms) {</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><span class="line"></span><br><span class="line"><span class="comment">// Get indirect symbol table (array of uint32_t indices into symbol table)</span></span><br><span class="line"> <span class="keyword">uint32_t</span> *indirect_symtab = (<span class="keyword">uint32_t</span> *)(linkedit_base + dysymtab_cmd->indirectsymoff);</span><br><span class="line"></span><br><span class="line"> cur = (<span class="keyword">uintptr_t</span>)header + <span class="keyword">sizeof</span>(<span class="keyword">mach_header_t</span>);</span><br><span class="line"> <span class="keyword">for</span> (uint i = <span class="number">0</span>; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {</span><br><span class="line"> cur_seg_cmd = (<span class="keyword">segment_command_t</span> *)cur;</span><br><span class="line"> <span class="keyword">if</span> (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(cur_seg_cmd->segname, SEG_DATA) != <span class="number">0</span> &&</span><br><span class="line"> <span class="built_in">strcmp</span>(cur_seg_cmd->segname, SEG_DATA_CONST) != <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (uint j = <span class="number">0</span>; j < cur_seg_cmd->nsects; j++) {</span><br><span class="line"> <span class="keyword">section_t</span> *sect =</span><br><span class="line"> (<span class="keyword">section_t</span> *)(cur + <span class="keyword">sizeof</span>(<span class="keyword">segment_command_t</span>)) + j;</span><br><span class="line"> <span class="keyword">if</span> ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) {</span><br><span class="line"> perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) {</span><br><span class="line"> perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);</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="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">perform_rebinding_with_section</span><span class="params">(struct rebindings_entry *rebindings,</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="keyword">section_t</span> *section,</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="keyword">intptr_t</span> slide,</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="keyword">nlist_t</span> *symtab,</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="keyword">char</span> *strtab,</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="keyword">uint32_t</span> *indirect_symtab)</span> </span>{</span><br><span class="line"> <span class="comment">// if _DATA,CONST</span></span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">bool</span> isDataConst = <span class="built_in">strcmp</span>(section->segname, SEG_DATA_CONST) == <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">//__la_symbol_ptr的reserved1字段标识了section描述的符号在符号表中开始的index</span></span><br><span class="line"> <span class="comment">//动态符号表中第一个需要解析的符号 开始地址</span></span><br><span class="line"> <span class="keyword">uint32_t</span> *indirect_symbol_indices = indirect_symtab + section->reserved1;</span><br><span class="line"> <span class="comment">// section __la_symbol_ptr</span></span><br><span class="line"> <span class="keyword">void</span> **indirect_symbol_bindings = (<span class="keyword">void</span> **)((<span class="keyword">uintptr_t</span>)slide + section->addr);</span><br><span class="line"> <span class="keyword">vm_prot_t</span> oldProtection = VM_PROT_READ;</span><br><span class="line"> <span class="comment">// chang memory protection to write && back old memery protection</span></span><br><span class="line"> <span class="keyword">if</span> (isDataConst) {</span><br><span class="line"> oldProtection = get_protection(rebindings);</span><br><span class="line"> mprotect(indirect_symbol_bindings, section->size, PROT_READ | PROT_WRITE);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// Traverse section -> symtab</span></span><br><span class="line"> <span class="keyword">for</span> (uint i = <span class="number">0</span>; i < section->size / <span class="keyword">sizeof</span>(<span class="keyword">void</span> *); i++) {</span><br><span class="line"> <span class="keyword">uint32_t</span> symtab_index = indirect_symbol_indices[i];</span><br><span class="line"> <span class="keyword">if</span> (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL ||</span><br><span class="line"> symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) {</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// nlist_t</span></span><br><span class="line"> <span class="keyword">uint32_t</span> strtab_offset = symtab[symtab_index].n_un.n_strx;</span><br><span class="line"> <span class="keyword">char</span> *symbol_name = strtab + strtab_offset;</span><br><span class="line"> <span class="keyword">bool</span> symbol_name_longer_than_1 = symbol_name[<span class="number">0</span>] && symbol_name[<span class="number">1</span>];</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">rebindings_entry</span> *<span class="title">cur</span> =</span> rebindings;</span><br><span class="line"> <span class="keyword">while</span> (cur) {</span><br><span class="line"> <span class="keyword">for</span> (uint j = <span class="number">0</span>; j < cur->rebindings_nel; j++) {</span><br><span class="line"> <span class="comment">// yes, it's target symbol to rebind!</span></span><br><span class="line"> <span class="keyword">if</span> (symbol_name_longer_than_1 &&</span><br><span class="line"> <span class="built_in">strcmp</span>(&symbol_name[<span class="number">1</span>], cur->rebindings[j].name) == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">if</span> (cur->rebindings[j].replaced != <span class="literal">NULL</span> &&</span><br><span class="line"> indirect_symbol_bindings[i] != cur->rebindings[j].replacement) {</span><br><span class="line"> *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i]; <span class="comment">// backup old func </span></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// do rebind, hook!</span></span><br><span class="line"> **indirect_symbol_bindings[i] = cur->rebindings[j].replacement;**</span><br><span class="line"> <span class="keyword">goto</span> symbol_loop;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> cur = cur->next;</span><br><span class="line"> }</span><br><span class="line"> symbol_loop:;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// restore protection</span></span><br><span class="line"> <span class="keyword">if</span> (isDataConst) {</span><br><span class="line"> <span class="keyword">int</span> protection = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span> (oldProtection & VM_PROT_READ) {</span><br><span class="line"> protection |= PROT_READ;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (oldProtection & VM_PROT_WRITE) {</span><br><span class="line"> protection |= PROT_WRITE;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (oldProtection & VM_PROT_EXECUTE) {</span><br><span class="line"> protection |= PROT_EXEC;</span><br><span class="line"> }</span><br><span class="line"> mprotect(indirect_symbol_bindings, section->size, protection);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="调试"><a href="#调试" class="headerlink" title="调试"></a>调试</h1><p>直接拿官方的demo编译出来调试分析流程:</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">~/study/ios_re_link/fishhook cat main.c</span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><fcntl.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><unistd.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdarg.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"fishhook.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="title">int</span> <span class="params">(*orig_close)</span><span class="params">(<span class="keyword">int</span>)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="title">int</span> <span class="params">(*orig_open)</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span> *, <span class="keyword">int</span>, ...)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">my_close</span><span class="params">(<span class="keyword">int</span> fd)</span> </span>{</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Calling real close(%d)\\n"</span>, fd);</span><br><span class="line"> <span class="keyword">return</span> orig_close(fd);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">my_open</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span> *path, <span class="keyword">int</span> oflag, ...)</span> </span>{</span><br><span class="line"> va_list ap = {<span class="number">0</span>};</span><br><span class="line"> <span class="keyword">mode_t</span> mode = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> ((oflag & O_CREAT) != <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">// mode only applies to O_CREAT</span></span><br><span class="line"> va_start(ap, oflag);</span><br><span class="line"> mode = va_arg(ap, <span class="keyword">int</span>);</span><br><span class="line"> va_end(ap);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Calling real open('%s', %d, %d)\\n"</span>, path, oflag, mode);</span><br><span class="line"> <span class="keyword">return</span> orig_open(path, oflag, mode);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Calling real open('%s', %d)\\n"</span>, path, oflag);</span><br><span class="line"> <span class="keyword">return</span> orig_open(path, oflag, mode);</span><br><span class="line"> }</span><br><span class="line">}</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">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"> getchar();</span><br><span class="line"> rebind_symbols((struct rebinding[<span class="number">2</span>]){{<span class="string">"close"</span>, my_close, (<span class="keyword">void</span> *)&orig_close}, {<span class="string">"open"</span>, my_open, (<span class="keyword">void</span> *)&orig_open}}, <span class="number">2</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Open our own binary and print out first 4 bytes (which is the same</span></span><br><span class="line"> <span class="comment">// for all Mach-O binaries on a given architecture)</span></span><br><span class="line"> <span class="keyword">int</span> fd = open(argv[<span class="number">0</span>], O_RDONLY);</span><br><span class="line"> <span class="keyword">uint32_t</span> magic_number = <span class="number">0</span>;</span><br><span class="line"> read(fd, &magic_number, <span class="number">4</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Mach-O Magic Number: %x \\n"</span>, magic_number);</span><br><span class="line"> close(fd);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">}%</span><br><span class="line"> ~/study/ios_re_link/fishhook cat Makefile</span><br><span class="line">all:</span><br><span class="line"> xcrun -sdk iphoneos clang main.c fishhook.c -o main -target arm64-apple-ios12<span class="number">.2</span></span><br><span class="line"> codesign -s <span class="string">"A64593A4DDFA3557CCEFF47FC8E688DCD3E6E455"</span> --entitlements entitlements.xml -f main</span><br><span class="line"></span><br><span class="line">push:</span><br><span class="line"> scp main root@<span class="number">10.2</span><span class="number">.5</span><span class="number">.0</span>:/tmp</span><br><span class="line"></span><br><span class="line">clean:</span><br><span class="line"> rm main</span><br></pre></td></tr></table></figure><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">* thread #<span class="number">1</span>, <span class="built_in">queue</span> = <span class="string">'com.apple.main-thread'</span>, stop reason = step over</span><br><span class="line"> frame #<span class="number">0</span>: <span class="number">0x0000000100a3f6ac</span> main`rebind_symbols_for_image(rebindings=<span class="number">0x00000001012005b0</span>, header=<span class="number">0x0000000100a38000</span>, slide=<span class="number">10715136</span>) at fishhook.c:<span class="number">187</span>:<span class="number">8</span></span><br><span class="line"> <span class="number">184</span> }</span><br><span class="line"> <span class="number">185</span> }</span><br><span class="line"> <span class="number">186</span></span><br><span class="line">-> <span class="number">187</span> <span class="keyword">if</span> (!symtab_cmd || !dysymtab_cmd || !linkedit_segment ||</span><br><span class="line"> <span class="number">188</span> !dysymtab_cmd->nindirectsyms) {</span><br><span class="line"> <span class="number">189</span> <span class="keyword">return</span>;</span><br><span class="line"> <span class="number">190</span> }</span><br><span class="line">Target <span class="number">0</span>: (main) stopped.</span><br><span class="line">(lldb) po symtab_cmd</span><br><span class="line"><span class="number">0x0000000100a38440</span></span><br><span class="line"></span><br><span class="line">(lldb) po dysymtab_cmd</span><br><span class="line"><span class="number">0x0000000100a38458</span></span><br><span class="line"></span><br><span class="line">(lldb) po linkedit_segment</span><br><span class="line"><span class="number">0x0000000100a383c8</span></span><br><span class="line"></span><br><span class="line">(lldb)</span><br></pre></td></tr></table></figure><p>然后找到 <code>LC_SEGMENT_64_DATA</code> 处理 <code>S_LAZY_SYMBOL_POINTERS</code> 和 <code>S_NON_LAZY_SYMBOL_POINTERS</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></pre></td><td class="code"><pre><span class="line">(lldb) n</span><br><span class="line">Process <span class="number">2046</span> stopped</span><br><span class="line">* thread #<span class="number">1</span>, <span class="built_in">queue</span> = <span class="string">'com.apple.main-thread'</span>, stop reason = step over</span><br><span class="line"> frame #<span class="number">0</span>: <span class="number">0x0000000100a3f828</span> main`rebind_symbols_for_image(rebindings=<span class="number">0x00000001012005b0</span>, header=<span class="number">0x0000000100a38000</span>, slide=<span class="number">10715136</span>) at fishhook.c:<span class="number">215</span>:<span class="number">42</span></span><br><span class="line"> <span class="number">212</span> perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);</span><br><span class="line"> <span class="number">213</span> }</span><br><span class="line"> <span class="number">214</span> <span class="keyword">if</span> ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) {</span><br><span class="line">-> <span class="number">215</span> perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);</span><br><span class="line"> <span class="number">216</span> }</span><br><span class="line"> <span class="number">217</span> }</span><br><span class="line"> <span class="number">218</span> }</span><br></pre></td></tr></table></figure><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/seg.png" alt="seg"></p><p>这里为了调试,重点关注 <code>S_LAZY_SYMBOL_POINTERS</code> 的处理</p><p>首先在rebind之前查看open符号</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></pre></td><td class="code"><pre><span class="line">(lldb) image lookup -va <span class="number">0x0000000100a3fee0</span></span><br><span class="line"> Address: main[<span class="number">0x0000000100007ee0</span>] (main.__TEXT.__stub_helper + <span class="number">180</span>)</span><br><span class="line"> Summary:</span><br><span class="line"> Module: file = <span class="string">"/private/var/tmp/main"</span>, arch = <span class="string">"arm64"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//....</span></span><br><span class="line">Process <span class="number">2046</span> resuming</span><br><span class="line">Process <span class="number">2046</span> stopped</span><br><span class="line">* thread #<span class="number">1</span>, <span class="built_in">queue</span> = <span class="string">'com.apple.main-thread'</span>, stop reason = breakpoint <span class="number">4.1</span></span><br><span class="line"> frame #<span class="number">0</span>: <span class="number">0x0000000100a3fbc8</span> main`perform_rebinding_with_section(rebindings=<span class="number">0x00000001012005b0</span>, section=<span class="number">0x0000000100a382d8</span>, slide=<span class="number">10715136</span>, symtab=<span class="number">0x0000000100a44210</span>, strtab=<span class="string">" "</span>, indirect_symtab=<span class="number">0x0000000100a44780</span>) at fishhook.c:<span class="number">135</span>:<span class="number">46</span></span><br><span class="line"> <span class="number">132</span> <span class="built_in">strcmp</span>(&symbol_name[<span class="number">1</span>], cur->rebindings[j].name) == <span class="number">0</span>) {</span><br><span class="line"> <span class="number">133</span> <span class="keyword">if</span> (cur->rebindings[j].replaced != <span class="literal">NULL</span> &&</span><br><span class="line"> <span class="number">134</span> indirect_symbol_bindings[i] != cur->rebindings[j].replacement) {</span><br><span class="line">-> <span class="number">135</span> *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i];</span><br><span class="line"> <span class="number">136</span> }</span><br><span class="line"> <span class="number">137</span> indirect_symbol_bindings[i] = cur->rebindings[j].replacement;</span><br><span class="line"> <span class="number">138</span> <span class="keyword">goto</span> symbol_loop;</span><br><span class="line">Target <span class="number">0</span>: (main) stopped.</span><br><span class="line">(lldb) <span class="function">p <span class="title">symbol_name</span></span></span><br><span class="line"><span class="function"><span class="params">(<span class="keyword">char</span> *)</span> $20 </span>= <span class="number">0x0000000100a44937</span> <span class="string">"_open"</span></span><br><span class="line">(lldb)</span><br></pre></td></tr></table></figure><p>首先备份了原函数地址,确保hook后可以通过 <code>orign_open</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></pre></td><td class="code"><pre><span class="line">(lldb) <span class="function">p <span class="title">i</span></span></span><br><span class="line"><span class="function"><span class="params">(uint)</span> $24 </span>= <span class="number">13</span></span><br><span class="line">(lldb) po indirect_symbol_bindings[<span class="number">13</span>]</span><br><span class="line"><span class="number">0x0000000100a3fee0</span></span><br><span class="line"></span><br><span class="line">(lldb) image lookup -va <span class="number">0x0000000100a3fee0</span></span><br><span class="line"> Address: main[<span class="number">0x0000000100007ee0</span>] (main.__TEXT.__stub_helper + <span class="number">180</span>)</span><br><span class="line"> Summary:</span><br><span class="line"> Module: file = <span class="string">"/private/var/tmp/main"</span>, arch = <span class="string">"arm64"</span></span><br><span class="line"></span><br><span class="line">(lldb)</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><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">Process 2046 stopped</span><br><span class="line">* thread <span class="comment">#1, queue = 'com.apple.main-thread', stop reason = step over</span></span><br><span class="line"> frame <span class="comment">#0: 0x0000000100a3fbfc main`perform_rebinding_with_section(rebindings=0x00000001012005b0, section=0x0000000100a382d8, slide=10715136, symtab=0x0000000100a44210, strtab=" ", indirect_symtab=0x0000000100a44780) at fishhook.c:137:41</span></span><br><span class="line"> 134 indirect_symbol_bindings[i] != cur->rebindings[j].replacement) {</span><br><span class="line"> 135 *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i];</span><br><span class="line"> 136 }</span><br><span class="line">-> 137 indirect_symbol_bindings[i] = cur->rebindings[j].replacement;</span><br><span class="line"> 138 goto symbol_loop;</span><br><span class="line"> 139 }</span><br><span class="line"> 140 }</span><br><span class="line">Target 0: (main) stopped.</span><br><span class="line">(lldb) image list |grep main</span><br><span class="line">[ 0] EAE1AE51-465A-32E0-8B3F-195FE2480F4F 0x0000000100a38000 /private/var/tmp/main</span><br><span class="line"> /System/Volumes/Data/Users/muhe/study/ios_re_link/fishhook/main.dSYM/Contents/Resources/DWARF/main(0x0000000100a38000)</span><br><span class="line">(lldb)</span><br><span class="line">(lldb) x/20gx indirect_symbol_bindings</span><br><span class="line">0x100a40018: 0x00000001d8642a68 0x0000000100a3fe50</span><br><span class="line">0x100a40028: 0x0000000100a3fe5c 0x0000000100a3fe68</span><br><span class="line">0x100a40038: 0x0000000100a3fe74 0x00000001d8581374</span><br><span class="line">0x100a40048: 0x0000000100a3f224 0x00000001d8581694</span><br><span class="line">0x100a40058: 0x0000000100a3fea4 0x00000001d860ae30</span><br><span class="line">0x100a40068: 0x00000001d871060c 0x00000001d873dd30</span><br><span class="line">0x100a40078: 0x0000000100a3fed4 0x0000000100a3f270</span><br><span class="line">0x100a40088: 0x0000000100a3feec 0x0000000100a3fef8</span><br><span class="line">0x100a40098: 0x00000001d873dfd0 0x0000000100a3ff10</span><br><span class="line">0x100a400a8: 0x0000000100d04498 0x0000000100a3ff72</span><br><span class="line">(lldb) x/gx 0x100a40078+8</span><br><span class="line">0x100a40080: 0x0000000100a3f270</span><br><span class="line">(lldb)</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line">>>> hex(0x100a40080-0x0000000100a38000)</span><br><span class="line"><span class="string">'0x8080'</span></span><br><span class="line">>>></span><br></pre></td></tr></table></figure><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/func_ptr.png" alt="func_ptr"></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></pre></td><td class="code"><pre><span class="line"><span class="number">0x100a3f448</span> <+<span class="number">176</span>>: bl <span class="number">0x100a3fd9c</span> ; symbol stub <span class="keyword">for</span>: close</span><br><span class="line">-> <span class="number">0x100a3f44c</span> <+<span class="number">180</span>>: adrp x8, <span class="number">1</span></span><br><span class="line"> <span class="number">0x100a3f450</span> <+<span class="number">184</span>>: ldr x8, [x8]</span><br><span class="line"> <span class="number">0x100a3f454</span> <+<span class="number">188</span>>: ldr x8, [x8]</span><br><span class="line"> <span class="number">0x100a3f458</span> <+<span class="number">192</span>>: ldur x10, [x29, #<span class="number">-0x8</span>]</span><br><span class="line"> <span class="number">0x100a3f45c</span> <+<span class="number">196</span>>: subs x8, x8, x10</span><br><span class="line"> <span class="number">0x100a3f460</span> <+<span class="number">200</span>>: b.ne <span class="number">0x100a3f478</span> ; <+<span class="number">224</span>> at main.c</span><br><span class="line"> <span class="number">0x100a3f464</span> <+<span class="number">204</span>>: mov w8, #<span class="number">0x0</span></span><br><span class="line"> <span class="number">0x100a3f468</span> <+<span class="number">208</span>>: mov x0, x8</span><br><span class="line"> <span class="number">0x100a3f46c</span> <+<span class="number">212</span>>: ldp x29, x30, [sp, #<span class="number">0x70</span>]</span><br><span class="line"> <span class="number">0x100a3f470</span> <+<span class="number">216</span>>: add sp, sp, #<span class="number">0x80</span> ; =<span class="number">0x80</span></span><br><span class="line"> <span class="number">0x100a3f474</span> <+<span class="number">220</span>>: ret</span><br><span class="line"> <span class="number">0x100a3f478</span> <+<span class="number">224</span>>: bl <span class="number">0x100a3fd60</span> ; symbol stub <span class="keyword">for</span>: __stack_chk_fail</span><br><span class="line">(lldb) dis -a <span class="number">0x100a3fd9c</span></span><br><span class="line">main`close:</span><br><span class="line"> <span class="number">0x100a3fd9c</span> <+<span class="number">0</span>>: nop</span><br><span class="line"> <span class="number">0x100a3fda0</span> <+<span class="number">4</span>>: ldr x16, #<span class="number">0x2a8</span> ; (<span class="keyword">void</span> *)<span class="number">0x0000000100a3f224</span>: my_close at /Users/muhe/study/ios_re_link/fishhook/main.c:<span class="number">10</span></span><br><span class="line"> <span class="number">0x100a3fda4</span> <+<span class="number">8</span>>: br x16</span><br></pre></td></tr></table></figure><h1 id="引用"><a href="#引用" class="headerlink" title="引用"></a>引用</h1><p><a href="https://github.com/facebook/fishhook">https://github.com/facebook/fishhook</a></p>]]></content>
<summary type="html"><h1 id="关于"><a href="#关于" class="headerlink" title="关于"></a>关于</h1><p>Fishhook是Facebook提供的利用MachO文件惰性加载原理,通过修改懒加载和非懒加载两个表的指针达到C函数HOOK的目的一个轻量级的hook库。理解这个工具和熟悉流程也是可以帮助更好的理解MachO文件格式 :)</p></summary>
<category term="iOS" scheme="https://o0xmuhe.github.io/categories/iOS/"/>
<category term="RE" scheme="https://o0xmuhe.github.io/tags/RE/"/>
<category term="iOS" scheme="https://o0xmuhe.github.io/tags/iOS/"/>
<category term="fishhook" scheme="https://o0xmuhe.github.io/tags/fishhook/"/>
</entry>
<entry>
<title>iOS RE 4 beginners 2 - 静态链接&&动态链接</title>
<link href="https://o0xmuhe.github.io/2021/07/14/iOS-RE-4-beginners-2/"/>
<id>https://o0xmuhe.github.io/2021/07/14/iOS-RE-4-beginners-2/</id>
<published>2021-07-14T09:15:47.000Z</published>
<updated>2021-07-20T08:08:05.162Z</updated>
<content type="html"><![CDATA[<h1 id="ENV"><a href="#ENV" class="headerlink" title="ENV"></a>ENV</h1><p>macos11.4 + iphone6 iOS 12.2</p><a id="more"></a><h1 id="静态链接"><a href="#静态链接" class="headerlink" title="静态链接"></a>静态链接</h1><p>静态链接:输入多个目标文件,输出一个文件(一般是可执行文件)。这个过程中,把多个目标文件里相同性质的段合并到一起。</p><h2 id="过程"><a href="#过程" class="headerlink" title="过程"></a>过程</h2><ul><li>地址和空间分配 (Address and Storage Allocation)</li><li>符号决议 (Symbol Resolution) / 符号绑定 (Symbol Binding)</li><li>重定位 (Relocation)</li></ul><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/Untitled.png" alt="%E9%93%BE%E6%8E%A5%E4%B8%8Edyld%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E4%BB%A5%E5%8F%8Afishhook%E5%8E%9F%E7%90%86%20notes%20ae172b61c1044420a207538366c9d075/Untitled.png"></p><h2 id="源码"><a href="#源码" class="headerlink" title="源码"></a>源码</h2><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">~/study/ios_re_link/static_link cat main.c</span><br><span class="line"></span><br><span class="line">extern int global_var;</span><br><span class="line"></span><br><span class="line">int foo(int i);</span><br><span class="line"></span><br><span class="line">int main(void){</span><br><span class="line"></span><br><span class="line"> int ret = foo(42 + global_var);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">return</span> 0;</span><br><span class="line">}</span><br><span class="line"> ~/study/ios_re_link/static_link cat foo.c</span><br><span class="line">int global_var = 0x1337;</span><br><span class="line"></span><br><span class="line">int foo(int i){</span><br><span class="line"> <span class="built_in">return</span> i + global_var;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><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">~/study/ios_re_link/static_link xcrun -sdk iphoneos clang -c main.c foo.c -target arm64-apple-ios12.2</span><br><span class="line">~/study/ios_re_link/static_link xcrun -sdk iphoneos clang main.o foo.o -o main -target arm64-apple-ios12.2</span><br></pre></td></tr></table></figure><p>两个模块(main.o 和 foo.o) 通过静态链接组合成了一个可执行文件(main)</p><h2 id="模块-amp-amp-产物"><a href="#模块-amp-amp-产物" class="headerlink" title="模块&&产物"></a>模块&&产物</h2><h3 id="main-o"><a href="#main-o" class="headerlink" title="main.o"></a>main.o</h3><p>通过machoview可以看到重定位段有三条信息,意味着程序中有三处需要重定位处理:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/Untitled%201.png" alt="%E9%93%BE%E6%8E%A5%E4%B8%8Edyld%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E4%BB%A5%E5%8F%8Afishhook%E5%8E%9F%E7%90%86%20notes%20ae172b61c1044420a207538366c9d075/Untitled%201.png"></p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/Untitled%202.png" alt="%E9%93%BE%E6%8E%A5%E4%B8%8Edyld%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E4%BB%A5%E5%8F%8Afishhook%E5%8E%9F%E7%90%86%20notes%20ae172b61c1044420a207538366c9d075/Untitled%202.png"></p><p>这个图是hopper反汇编的main函数,可以看到对于引用到其他模块(foo.o)重的变量/函数的地方看起来“正常”,但是点击 <code>bl _foo</code> 就会发现跳转到了:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/Untitled%203.png" alt="%E9%93%BE%E6%8E%A5%E4%B8%8Edyld%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E4%BB%A5%E5%8F%8Afishhook%E5%8E%9F%E7%90%86%20notes%20ae172b61c1044420a207538366c9d075/Untitled%203.png"></p><p>根据<macho/reloc.h>的定义,可以看到reloc段的结构:</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></pre></td><td class="code"><pre><span class="line">struct relocation_info {</span><br><span class="line"> int32_t r_address; /* offset <span class="keyword">in</span> the section to what is being</span><br><span class="line"> relocated */</span><br><span class="line"> uint32_t r_symbolnum:24, /* symbol index <span class="keyword">if</span> r_extern == 1 or section</span><br><span class="line"> ordinal <span class="keyword">if</span> r_extern == 0 */</span><br><span class="line"> r_pcrel:1, /* was relocated pc relative already */</span><br><span class="line"> r_length:2, /* 0=byte, 1=word, 2=long, 3=quad */</span><br><span class="line"> r_extern:1, /* does not include value of sym referenced */</span><br><span class="line"> r_type:4; /* <span class="keyword">if</span> not 0, machine specific relocation <span class="built_in">type</span> */</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>结合上面的图来看(以_foo符号为例):</p><ul><li>r_address : 0x28</li><li>r_symbolnum(24bits): 指向_foo 字符串</li><li>剩下的8bits是标志位</li></ul><p>对应到汇编里就是,main函数的0x28行引用了 _foo 符号,reloc段把这个信息告知linker,这样在链接的时候linker就会处理这条信息,把对应的符号做替换处理。</p><h3 id="foo-o"><a href="#foo-o" class="headerlink" title="foo.o"></a>foo.o</h3><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/Untitled%204.png" alt="%E9%93%BE%E6%8E%A5%E4%B8%8Edyld%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E4%BB%A5%E5%8F%8Afishhook%E5%8E%9F%E7%90%86%20notes%20ae172b61c1044420a207538366c9d075/Untitled%204.png"></p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/Untitled%205.png" alt="%E9%93%BE%E6%8E%A5%E4%B8%8Edyld%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E4%BB%A5%E5%8F%8Afishhook%E5%8E%9F%E7%90%86%20notes%20ae172b61c1044420a207538366c9d075/Untitled%205.png"></p><p>其实都是对 <code>global_var</code>的引用</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/Untitled%206.png" alt="%E9%93%BE%E6%8E%A5%E4%B8%8Edyld%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E4%BB%A5%E5%8F%8Afishhook%E5%8E%9F%E7%90%86%20notes%20ae172b61c1044420a207538366c9d075/Untitled%206.png"></p><p>在foo.o模块中,是 0x20处的data,这个信息也要告诉linker,在link的阶段做替换。</p><h3 id="main"><a href="#main" class="headerlink" title="main"></a>main</h3><p>最终的可执行文件main,可以看到没有重定位信息,而且mian和foo函数中改替换的符号都已经完成了替换,可以顺利的索引到想要使用的符号(foo和global_var)。</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/Untitled%207.png" alt="%E9%93%BE%E6%8E%A5%E4%B8%8Edyld%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E4%BB%A5%E5%8F%8Afishhook%E5%8E%9F%E7%90%86%20notes%20ae172b61c1044420a207538366c9d075/Untitled%207.png"></p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/Untitled%208.png" alt="%E9%93%BE%E6%8E%A5%E4%B8%8Edyld%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E4%BB%A5%E5%8F%8Afishhook%E5%8E%9F%E7%90%86%20notes%20ae172b61c1044420a207538366c9d075/Untitled%208.png"></p><p>对比两者符号表:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/Untitled%209.png" alt="%E9%93%BE%E6%8E%A5%E4%B8%8Edyld%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E4%BB%A5%E5%8F%8Afishhook%E5%8E%9F%E7%90%86%20notes%20ae172b61c1044420a207538366c9d075/Untitled%209.png"></p><p>以foo符号为例 : </p><p>Type 从 N_UNDF → NSECT</p><p>Value 从0 → 0x100007f90 </p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/CleanShot%202021-07-20%20at%[email protected]" alt="CleanShot 2021-07-20 at 15.17.57@2x"></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><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">struct nlist_64 {</span><br><span class="line"> union {</span><br><span class="line"> uint32_t n_strx; /* index into the string table */</span><br><span class="line"> } n_un;</span><br><span class="line"> uint8_t n_type; /* <span class="built_in">type</span> flag, see below */</span><br><span class="line"> uint8_t n_sect; /* section number or NO_SECT */</span><br><span class="line"> uint16_t n_desc; /* see <mach-o/stab.h> */</span><br><span class="line"> uint64_t n_value; /* value of this symbol (or stab offset) */</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>foo 符号的话</p><ul><li>string table index : 指向符号的字符串</li><li>n_sect : 改符号在第几个section</li><li>n_value : 符号具体值(地址)</li></ul><h2 id="举个🌰"><a href="#举个🌰" class="headerlink" title="举个🌰"></a>举个🌰</h2><p>这里以demo中 global_var 使用的代码举例子。</p><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">int ret = foo(42 + global_var);</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><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><br><span class="line">0000000000000014 adrp x9, <span class="comment">#0x0 ; 0x68@PAGE</span></span><br><span class="line">0000000000000018 ldr x9, [x9, <span class="comment">#0x68] ; 0x68@PAGEOFF</span></span><br><span class="line">000000000000001c ldr w10, [x9]</span><br><span class="line">0000000000000020 add w0, w10, <span class="comment">#0x2a</span></span><br><span class="line">0000000000000024 str w8, [sp, <span class="comment">#0x10 + var_C]</span></span><br><span class="line">0000000000000028 bl _foo</span><br></pre></td></tr></table></figure><p>可知 w0 是参数,w10是global_var的值,来自x9</p><p><code>w10 = [x9 + 0x68]</code> (未重定位修复)</p><p>最开始索引x9的时候可以发现是把0赋给了x9,因为这里还没有重定位,所以用0代替。</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><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">0000000100007f64 adrp x9, <span class="comment">#0x100008000 ; 0x100008000@PAGE</span></span><br><span class="line">0000000100007f68 add x9, x9, <span class="comment">#0x0 ; 0x100008000@PAGEOFF, _global_var</span></span><br><span class="line">0000000100007f6c ldr w10, [x9] ; _global_var</span><br><span class="line">0000000100007f70 add w0, w10, <span class="comment">#0x2a</span></span><br><span class="line">0000000100007f74 str w8, [sp, <span class="comment">#0x10 + var_C]</span></span><br><span class="line">0000000100007f78 bl _foo</span><br></pre></td></tr></table></figure><p>把0替换成了 0x100008000,这个地址恰好指向global_var。</p><p>可以看到经过linker的处理,可以正确找到global_var,符号foo同理</p><h1 id="动态链接"><a href="#动态链接" class="headerlink" title="动态链接"></a>动态链接</h1><h2 id="debug-set-up"><a href="#debug-set-up" class="headerlink" title="debug set up"></a>debug set up</h2><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/CleanShot%202021-07-20%20at%[email protected]" alt="CleanShot 2021-07-20 at 15.18.24@2x"></p><p>应该是签名有问题,最终解决方案:</p><figure class="highlight html"><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></pre></td><td class="code"><pre><span class="line">/usr/bin/security find-identity -v -p codesigning</span><br><span class="line"># get : A64593A4DDFA3557CCEFF47FC8E688DCD3E6E455</span><br><span class="line"></span><br><span class="line">codesign -s "A64593A4DDFA3557CCEFF47FC8E688DCD3E6E455" --entitlements entitlements.xml -f libFoo.dylib</span><br><span class="line">codesign -s "A64593A4DDFA3557CCEFF47FC8E688DCD3E6E455" --entitlements entitlements.xml -f main</span><br><span class="line"></span><br><span class="line"># scp ....</span><br><span class="line"># ssh ....</span><br><span class="line">mude-iPhone:/tmp root# ./main</span><br><span class="line">magic is : 4919</span><br><span class="line">4920</span><br></pre></td></tr></table></figure><h2 id="debug-lazy-binding-process"><a href="#debug-lazy-binding-process" class="headerlink" title="debug lazy binding process"></a>debug lazy binding process</h2><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/CleanShot%202021-07-20%20at%[email protected]" alt="CleanShot 2021-07-20 at 15.19.30@2x"></p><p>可以看到,第一次调用 <code>printf</code>的时候,bl跳过去并不是 printf函数</p><figure class="highlight plain"><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">Target 0: (main) stopped.</span><br><span class="line">(lldb) s</span><br><span class="line">Process 1453 stopped</span><br><span class="line">* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into</span><br><span class="line"> frame #0: 0x000000010089bf7c main</span><br><span class="line">-> 0x10089bf7c: br x16</span><br><span class="line"> 0x10089bf80: ldr w16, 0x10089bf88</span><br><span class="line"> 0x10089bf84: b 0x10089bf68</span><br><span class="line"> 0x10089bf88: udf #0x0</span><br><span class="line">Target 0: (main) stopped.</span><br><span class="line">(lldb) re re x16</span><br><span class="line"> x16 = 0x00000001d858080c libdyld.dylib`dyld_stub_binder</span><br><span class="line">(lldb) re re x0</span><br><span class="line"> x0 = 0x000000010089bfa4 "magic is : %d\n"</span><br><span class="line">(lldb) re re x1</span><br><span class="line"> x1 = 0x0000000000001337</span><br></pre></td></tr></table></figure><p>通过 <code>dyld_stub_binder</code> 找 <code>printf</code>的地址,把找到的地址写回到 <code>DATA,__la_symbol_ptr</code></p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/CleanShot%202021-07-20%20at%[email protected]" alt="CleanShot 2021-07-20 at 15.20.15@2x"></p><p>第二次调用printf的时候就可以看到,这个地方printf函数地址已经被写过来了</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">(lldb) x/10i <span class="variable">$pc</span></span><br><span class="line">-> 0x100dcff60: 0x58000610 ldr x16, <span class="comment">#0xc0 ; (void *)0x00000001d860e14c: printf</span></span><br><span class="line"> 0x100dcff64: 0xd61f0200 br x16</span><br><span class="line"> 0x100dcff68: 0x10000611 adr x17, <span class="comment">#0xc0 ; _dyld_private</span></span><br><span class="line"> 0x100dcff6c: 0xd503201f nop</span><br><span class="line"> 0x100dcff70: 0xa9bf47f0 stp x16, x17, [sp, <span class="comment">#-0x10]!</span></span><br><span class="line"> 0x100dcff74: 0xd503201f nop</span><br><span class="line"> 0x100dcff78: 0x58000490 ldr x16, <span class="comment">#0x90 ; (void *)0x00000001d858080c: dyld_stub_binder</span></span><br><span class="line"> 0x100dcff7c: 0xd61f0200 br x16</span><br><span class="line"> 0x100dcff80: 0x18000050 ldr w16, 0x100dcff88</span><br><span class="line"> 0x100dcff84: 0x17fffff9 b 0x100dcff68</span><br><span class="line">(lldb) x/3i <span class="variable">$pc</span></span><br><span class="line">-> 0x100dcff60: 0x58000610 ldr x16, <span class="comment">#0xc0 ; (void *)0x00000001d860e14c: printf</span></span><br><span class="line"> 0x100dcff64: 0xd61f0200 br x16</span><br><span class="line"> 0x100dcff68: 0x10000611 adr x17, <span class="comment">#0xc0 ; _dyld_private</span></span><br><span class="line">(lldb) x/gx <span class="variable">$pc</span>+0xc0</span><br><span class="line">0x100dd0020: 0x00000001d860e14c</span><br><span class="line">(lldb) memory region 0x00000001d860e14c</span><br></pre></td></tr></table></figure><p>所以这里就可以直接获取到地址,然后直接跳转过去就行:</p><figure class="highlight html"><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">(lldb) s</span><br><span class="line">Process 1453 stopped</span><br><span class="line">* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into</span><br><span class="line"> frame #0: 0x000000010089bf60 main`printf + 4</span><br><span class="line">main`printf:</span><br><span class="line">-> 0x10089bf60 <span class="tag"><<span class="name">+4</span>></span>: ldr x16, #0xc0 ; (void *)0x00000001d860e14c: printf</span><br><span class="line"> 0x10089bf64 <span class="tag"><<span class="name">+8</span>></span>: br x16</span><br><span class="line"> 0x10089bf68: adr x17, #0xc0 ; _dyld_private</span><br><span class="line"> 0x10089bf6c: nop</span><br><span class="line">Target 0: (main) stopped.</span><br><span class="line">(lldb) s</span><br><span class="line">Process 1453 stopped</span><br><span class="line">* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into</span><br><span class="line"> frame #0: 0x000000010089bf64 main`printf + 8</span><br><span class="line">main`printf:</span><br><span class="line">-> 0x10089bf64 <span class="tag"><<span class="name">+8</span>></span>: br x16</span><br><span class="line"> 0x10089bf68: adr x17, #0xc0 ; _dyld_private</span><br><span class="line"> 0x10089bf6c: nop</span><br><span class="line"> 0x10089bf70: stp x16, x17, [sp, #-0x10]!</span><br><span class="line">Target 0: (main) stopped.</span><br><span class="line">(lldb) re re x16</span><br><span class="line"> x16 = 0x00000001d860e14c libsystem_c.dylib`printf</span><br><span class="line">(lldb)</span><br></pre></td></tr></table></figure><h2 id="libdyld-dylib-dyld-stub-binder"><a href="#libdyld-dylib-dyld-stub-binder" class="headerlink" title="libdyld.dylib`dyld_stub_binder"></a>libdyld.dylib`dyld_stub_binder</h2><p>dyld-852的代码:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/CleanShot%202021-07-20%20at%[email protected]" alt="CleanShot 2021-07-20 at 15.20.35@2x"></p><p>因为我目标环境是iOS12.2,所以具体汇编代码有一些差别:</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">Target 0: (main) stopped.</span><br><span class="line">(lldb) x/30i $pc</span><br><span class="line">-> 0x1d858080c: 0xa9bf7bfd stp x29, x30, [sp, #-0x10]!</span><br><span class="line"> 0x1d8580810: 0x910003fd mov x29, sp</span><br><span class="line"> 0x1d8580814: 0xd103c3ff sub sp, sp, #0xf0 ; =0xf0</span><br><span class="line"> 0x1d8580818: 0xa93f07a0 stp x0, x1, [x29, #-0x10]</span><br><span class="line"> 0x1d858081c: 0xa93e0fa2 stp x2, x3, [x29, #-0x20]</span><br><span class="line"> 0x1d8580820: 0xa93d17a4 stp x4, x5, [x29, #-0x30]</span><br><span class="line"> 0x1d8580824: 0xa93c1fa6 stp x6, x7, [x29, #-0x40]</span><br><span class="line"> 0x1d8580828: 0xa93b27a8 stp x8, x9, [x29, #-0x50]</span><br><span class="line"> 0x1d858082c: 0xad3c07a0 stp q0, q1, [x29, #-0x80]</span><br><span class="line"> 0x1d8580830: 0xad3b0fa2 stp q2, q3, [x29, #-0xa0]</span><br><span class="line"> 0x1d8580834: 0xad3a17a4 stp q4, q5, [x29, #-0xc0]</span><br><span class="line"> 0x1d8580838: 0xad391fa6 stp q6, q7, [x29, #-0xe0]</span><br><span class="line"> 0x1d858083c: 0xf9400fa0 ldr x0, [x29, #0x18]</span><br><span class="line"> 0x1d8580840: 0xf9400ba1 ldr x1, [x29, #0x10]</span><br><span class="line"> 0x1d8580844: 0x940004e4 bl 0x1d8581bd4 ; _dyld_fast_stub_entry(void*, long)</span><br><span class="line"> 0x1d8580848: 0xaa0003f0 mov x16, x0</span><br><span class="line"> 0x1d858084c: 0xa97f07a0 ldp x0, x1, [x29, #-0x10]</span><br><span class="line"> 0x1d8580850: 0xa97e0fa2 ldp x2, x3, [x29, #-0x20]</span><br><span class="line"> 0x1d8580854: 0xa97d17a4 ldp x4, x5, [x29, #-0x30]</span><br><span class="line"> 0x1d8580858: 0xa97c1fa6 ldp x6, x7, [x29, #-0x40]</span><br><span class="line"> 0x1d858085c: 0xa97b27a8 ldp x8, x9, [x29, #-0x50]</span><br><span class="line"> 0x1d8580860: 0xad7c07a0 ldp q0, q1, [x29, #-0x80]</span><br><span class="line"> 0x1d8580864: 0xad7b0fa2 ldp q2, q3, [x29, #-0xa0]</span><br><span class="line"> 0x1d8580868: 0xad7a17a4 ldp q4, q5, [x29, #-0xc0]</span><br><span class="line"> 0x1d858086c: 0xad791fa6 ldp q6, q7, [x29, #-0xe0]</span><br><span class="line"> 0x1d8580870: 0x910003bf mov sp, x29</span><br><span class="line"> 0x1d8580874: 0xa8c17bfd ldp x29, x30, [sp], #0x10</span><br><span class="line"> 0x1d8580878: 0x910043ff add sp, sp, #0x10 ; =0x10</span><br><span class="line"> 0x1d858087c: 0xd61f0200 br x16</span><br><span class="line"> 0x1d8580880: 0xd10103ff sub sp, sp, #0x40 ; =0x40</span><br></pre></td></tr></table></figure><p>但是本质上是差不多的,影响不大。</p><p>下面看看怎么一步一步调用进去,找到所需要的符号</p><h3 id="1-call-dyld-stub-binder"><a href="#1-call-dyld-stub-binder" class="headerlink" title="1. call dyld_stub_binder"></a>1. call dyld_stub_binder</h3><figure class="highlight plain"><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">0000000100007f98 ldr w16, =0x6967616d0000001a</span><br><span class="line">0000000100007f9c b 0x100007f68</span><br><span class="line"></span><br><span class="line">....</span><br><span class="line"></span><br><span class="line">0000000100007f68 adr x17, #0x100008028 ; CODE XREF=0x100007f84, 0x100007f90, 0x100007f9c</span><br><span class="line">// x17-> _dyld_private</span><br><span class="line"></span><br><span class="line">0000000100007f6c nop</span><br><span class="line">0000000100007f70 stp x16, x17, [sp, #-0x10]!</span><br><span class="line"></span><br><span class="line">0000000100007f74 nop</span><br><span class="line">0000000100007f78 ldr x16, #dyld_stub_binder_100008008</span><br><span class="line"></span><br><span class="line">0000000100007f7c br x16 // call dyld_stub_binder</span><br></pre></td></tr></table></figure><p>个人猜测:<code>0x000000000000001a</code> 应该是 类似 linux下elf lazy binding的时候那个index参数的东西,每个符号都不一样 。</p><p>初始化好需要的参数就调用进去dyld中去做符号绑定操作了</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><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">(lldb) s</span><br><span class="line">Process 1465 stopped</span><br><span class="line">* thread <span class="comment">#1, queue = 'com.apple.main-thread', stop reason = instruction step into</span></span><br><span class="line"> frame <span class="comment">#0: 0x0000000100fb7f7c main</span></span><br><span class="line">-> 0x100fb7f7c: br x16</span><br><span class="line"> 0x100fb7f80: ldr w16, 0x100fb7f88</span><br><span class="line"> 0x100fb7f84: b 0x100fb7f68</span><br><span class="line"> 0x100fb7f88: udf <span class="comment">#0x0</span></span><br><span class="line">Target 0: (main) stopped.</span><br><span class="line">(lldb) re re x16</span><br><span class="line"> x16 = 0x00000001d858080c libdyld.dylib`dyld_stub_binder</span><br><span class="line">(lldb) x/10gx <span class="variable">$sp</span></span><br><span class="line">0x16ee4f5a0: 0x000000000000001a 0x0000000100fb8028</span><br><span class="line">0x16ee4f5b0: 0x0000000000001337 0x0000000000000000</span><br><span class="line">0x16ee4f5c0: 0x0000000000000000 0x0000000000000001</span><br><span class="line">0x16ee4f5d0: 0x000000016ee4f5f0 0x00000001d857e8e0</span><br><span class="line">0x16ee4f5e0: 0x00000001d857e8e0 0x0000000000000000</span><br><span class="line">(lldb) re re x0</span><br><span class="line"> x0 = 0x0000000100fb7fa4 <span class="string">"magic is : %d\n"</span></span><br><span class="line">(lldb) re re x1</span><br><span class="line"> x1 = 0x0000000000001337</span><br><span class="line">(lldb) re re x2</span><br><span class="line"> x2 = 0x00000000000120a8</span><br></pre></td></tr></table></figure><h3 id="2-call-dyld-fastBindLazySymbol-loadercache-lazyinfo"><a href="#2-call-dyld-fastBindLazySymbol-loadercache-lazyinfo" class="headerlink" title="2. call dyld::fastBindLazySymbol(loadercache, lazyinfo)"></a>2. call dyld::fastBindLazySymbol(loadercache, lazyinfo)</h3><p>保存栈帧,保存当前的寄存器信息(一大堆stp指令,后面符号绑定完成后,ldp会恢复,这些是成对的),然后设置好参数,就直接转到 <code>dyld::fastBindLazySymbol</code></p><p>(函数前面的保存操作看起来和x86上函数开头的保存栈帧 抬高栈給临时变量预留空间的操作差不多)</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></pre></td><td class="code"><pre><span class="line">Process 1465 resuming</span><br><span class="line">Process 1465 stopped</span><br><span class="line">* thread <span class="comment">#1, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1</span></span><br><span class="line"> frame <span class="comment">#0: 0x00000001d8580844 libdyld.dylib`dyld_stub_binder + 56</span></span><br><span class="line">libdyld.dylib`dyld_stub_binder:</span><br><span class="line">-> 0x1d8580844 <+56>: bl 0x1d8581bd4 ; _dyld_fast_stub_entry(void*, long)</span><br><span class="line"> 0x1d8580848 <+60>: mov x16, x0</span><br><span class="line"> 0x1d858084c <+64>: ldp x0, x1, [x29, <span class="comment">#-0x10]</span></span><br><span class="line"> 0x1d8580850 <+68>: ldp x2, x3, [x29, <span class="comment">#-0x20]</span></span><br><span class="line">Target 0: (main) stopped.</span><br><span class="line">(lldb) re re x0</span><br><span class="line"> x0 = 0x0000000100fb8028 _dyld_private</span><br><span class="line">(lldb) re re x1</span><br><span class="line"> x1 = 0x000000000000001a</span><br></pre></td></tr></table></figure><p>调用的是 : <code>fastBindLazySymbol(0x0000000100fb8028, 0x1a)</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// LINK_EDIT seg</span></span><br><span class="line"><span class="keyword">const</span> <span class="keyword">uint8_t</span>* <span class="keyword">const</span> start = fLinkEditBase + fDyldInfo->lazy_bind_off;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">uint8_t</span>* <span class="keyword">const</span> <span class="built_in">end</span> = &start[fDyldInfo->lazy_bind_size];</span><br><span class="line"></span><br><span class="line"><span class="comment">// ....</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">do</span>{</span><br><span class="line"> <span class="keyword">if</span> ( ! getLazyBindingInfo(lazyBindingInfoOffset, start, <span class="built_in">end</span>, &segIndex, &segOffset, &libraryOrdinal, &symbolName, &doneAfterBind) )</span><br><span class="line"> dyld::throwf(<span class="string">"bad lazy bind info"</span>);</span><br><span class="line"></span><br><span class="line">}<span class="keyword">while</span> (!doneAfterBind && !context.strictMachORequired);</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><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">(lldb) c</span><br><span class="line"><span class="built_in">Process</span> <span class="number">1465</span> resuming</span><br><span class="line"><span class="built_in">Process</span> <span class="number">1465</span> stopped</span><br><span class="line">* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1</span><br><span class="line"> frame #<span class="number">0</span>: <span class="number">0x0000000100fe5e6c</span> dyld`ImageLoaderMachOCompressed::doBindFastLazySymbol(<span class="keyword">unsigned</span> <span class="keyword">int</span>, ImageLoader::LinkContext <span class="keyword">const</span>&, <span class="keyword">void</span> (*)(), <span class="keyword">void</span> (*)()) + <span class="number">136</span></span><br><span class="line">dyld`ImageLoaderMachOCompressed::doBindFastLazySymbol:</span><br><span class="line">-> <span class="number">0x100fe5e6c</span> <+<span class="number">136</span>>: bl <span class="number">0x100fe1d98</span> ; ImageLoaderMachO::getLazyBindingInfo(<span class="keyword">unsigned</span> <span class="keyword">int</span>&, <span class="keyword">unsigned</span> <span class="keyword">char</span> <span class="keyword">const</span>*, <span class="keyword">unsigned</span> <span class="keyword">char</span> <span class="keyword">const</span>*, <span class="keyword">unsigned</span> <span class="keyword">char</span>*, <span class="keyword">unsigned</span> <span class="keyword">long</span>*, <span class="keyword">int</span>*, <span class="keyword">char</span> <span class="keyword">const</span>**, <span class="keyword">bool</span>*)</span><br><span class="line"> <span class="number">0x100fe5e70</span> <+<span class="number">140</span>>: tbz w0, #<span class="number">0x0</span>, <span class="number">0x100fe5f80</span> ; <+<span class="number">412</span>></span><br><span class="line"> <span class="number">0x100fe5e74</span> <+<span class="number">144</span>>: ldrb w1, [sp, #<span class="number">0x43</span>]</span><br><span class="line"> <span class="number">0x100fe5e78</span> <+<span class="number">148</span>>: ldrb w8, [x20, #<span class="number">0x74</span>]</span><br><span class="line">Target <span class="number">0</span>: (main) stopped.</span><br><span class="line">(lldb) re re x1</span><br><span class="line"> x1 = <span class="number">0x0000000100fbc030</span></span><br><span class="line">(lldb) re re x2</span><br><span class="line"> x2 = <span class="number">0x0000000100fbc058</span></span><br><span class="line">(lldb) memory region <span class="number">0x0000000100fbc030</span></span><br><span class="line">[<span class="number">0x0000000100fbc000</span><span class="number">-0x0000000100fc0000</span>) r-- __LINKEDIT</span><br><span class="line"></span><br><span class="line">(lldb)</span><br></pre></td></tr></table></figure><p>这里用到了 我这个可执行文件的LINK_EDIT 段去做符号绑定工作:</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">(lldb) <span class="built_in">image</span> lookup -va $x1</span><br><span class="line"> Address: main[<span class="number">0x000000010000c030</span>] (main.__LINKEDIT + <span class="number">48</span>)</span><br><span class="line"> Summary:</span><br><span class="line"> Module: file = <span class="string">"/private/var/tmp/main"</span>, arch = <span class="string">"arm64"</span></span><br></pre></td></tr></table></figure><h3 id="3-ImageLoaderMachO-getLazyBindingInfo"><a href="#3-ImageLoaderMachO-getLazyBindingInfo" class="headerlink" title="3. ImageLoaderMachO::getLazyBindingInfo"></a>3. ImageLoaderMachO::getLazyBindingInfo</h3><p>根据不同的opcode,走不同分支:</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="keyword">if</span> ( lazyBindingInfoOffset > (lazyInfoEnd-lazyInfoStart) )</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">bool</span> done = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">uint8_t</span>* p = &lazyInfoStart[lazyBindingInfoOffset];</span><br><span class="line"> <span class="keyword">while</span> ( !done && (p < lazyInfoEnd) ) {</span><br><span class="line"> <span class="keyword">uint8_t</span> immediate = *p & BIND_IMMEDIATE_MASK;</span><br><span class="line"> <span class="keyword">uint8_t</span> opcode = *p & BIND_OPCODE_MASK;</span><br><span class="line"> ++p;</span><br><span class="line"> <span class="keyword">switch</span> (opcode) {</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>获取目标符号相关的信息 :</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">&segIndex, &segOffset, &libraryOrdinal, &symbolName, &doneAfterBind</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">uintptr_t</span> address = segActualLoadAddress(segIndex) + segOffset;</span><br></pre></td></tr></table></figure><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="comment">// dyld版本不一致,实现的函数有些差别,但是本质是一样的</span></span><br><span class="line">(lldb) n</span><br><span class="line"><span class="built_in">Process</span> <span class="number">1465</span> stopped</span><br><span class="line">* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over</span><br><span class="line"> frame #<span class="number">0</span>: <span class="number">0x0000000100fe5ee4</span> dyld`ImageLoaderMachOCompressed::doBindFastLazySymbol(<span class="keyword">unsigned</span> <span class="keyword">int</span>, ImageLoader::LinkContext <span class="keyword">const</span>&, <span class="keyword">void</span> (*)(), <span class="keyword">void</span> (*)()) + <span class="number">256</span></span><br><span class="line">dyld`ImageLoaderMachOCompressed::doBindFastLazySymbol:</span><br><span class="line">-> <span class="number">0x100fe5ee4</span> <+<span class="number">256</span>>: mov x26, x0</span><br><span class="line"> <span class="number">0x100fe5ee8</span> <+<span class="number">260</span>>: mov x0, x20</span><br><span class="line"> <span class="number">0x100fe5eec</span> <+<span class="number">264</span>>: bl <span class="number">0x100fe1fb0</span> ; ImageLoaderMachO::imageBaseAddress() <span class="keyword">const</span></span><br><span class="line"> <span class="number">0x100fe5ef0</span> <+<span class="number">268</span>>: mov x1, x0</span><br><span class="line">Target <span class="number">0</span>: (main) stopped.</span><br><span class="line">(lldb) re re x0</span><br><span class="line"> x0 = <span class="number">0x00000001d860e14c</span> libsystem_c.dylib`<span class="built_in">printf</span></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></pre></td><td class="code"><pre><span class="line">result = bindAt(context, <span class="keyword">this</span>, address, BIND_TYPE_POINTER, symbolName, <span class="number">0</span>, <span class="number">0</span>, libraryOrdinal,<span class="literal">NULL</span>, <span class="string">"lazy "</span>, patcher, <span class="literal">NULL</span>, <span class="literal">true</span>);</span><br></pre></td></tr></table></figure><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">// 调试:</span></span><br><span class="line">frame #<span class="number">0</span>: <span class="number">0x0000000100fe5f28</span> dyld`ImageLoaderMachOCompressed::doBindFastLazySymbol(<span class="keyword">unsigned</span> <span class="keyword">int</span>, ImageLoader::LinkContext <span class="keyword">const</span>&, <span class="keyword">void</span> (*)(), <span class="keyword">void</span> (*)()) + <span class="number">324</span></span><br><span class="line">dyld`ImageLoaderMachOCompressed::doBindFastLazySymbol:</span><br><span class="line">-> <span class="number">0x100fe5f28</span> <+<span class="number">324</span>>: bl <span class="number">0x100fe0664</span> ; ImageLoaderMachO::bindLocation(ImageLoader::LinkContext <span class="keyword">const</span>&, <span class="keyword">unsigned</span> <span class="keyword">long</span>, <span class="keyword">unsigned</span> <span class="keyword">long</span>, <span class="keyword">unsigned</span> <span class="keyword">long</span>, <span class="keyword">unsigned</span> <span class="keyword">char</span>, <span class="keyword">char</span> <span class="keyword">const</span>*, <span class="keyword">long</span>, <span class="keyword">char</span> <span class="keyword">const</span>*, <span class="keyword">char</span> <span class="keyword">const</span>*, <span class="keyword">char</span> <span class="keyword">const</span>*, ImageLoaderMachO::ExtraBindData*, <span class="keyword">unsigned</span> <span class="keyword">long</span>)</span><br><span class="line"> <span class="number">0x100fe5f2c</span> <+<span class="number">328</span>>: ldrb w8, [sp, #<span class="number">0x27</span>]</span><br><span class="line"> <span class="number">0x100fe5f30</span> <+<span class="number">332</span>>: ldrb w9, [x21, #<span class="number">0x139</span>]</span><br><span class="line"> <span class="number">0x100fe5f34</span> <+<span class="number">336</span>>: orr w8, w8, w9</span><br><span class="line">Target <span class="number">0</span>: (main) stopped.</span><br><span class="line">(lldb) re re x0</span><br><span class="line"> x0 = <span class="number">0x00000001010235e0</span> dyld::gLinkContext</span><br><span class="line">(lldb) re re x1</span><br><span class="line"> x1 = <span class="number">0x0000000100000000</span></span><br><span class="line">(lldb) re re x2</span><br><span class="line"> x2 = <span class="number">0x0000000100fb8020</span> (<span class="keyword">void</span> *)<span class="number">0x0000000100fb7f98</span></span><br><span class="line">(lldb) re re x3</span><br><span class="line"> x3 = <span class="number">0x00000001d860e14c</span> libsystem_c.dylib`<span class="built_in">printf</span></span><br><span class="line">(lldb) re re x4</span><br><span class="line"> x4 = <span class="number">0x0000000000000001</span></span><br><span class="line">(lldb) re re x5</span><br><span class="line"> x5 = <span class="number">0x0000000100fbc04e</span></span><br><span class="line">(lldb) re re x6</span><br><span class="line"> x6 = <span class="number">0x0000000000000000</span></span><br><span class="line">(lldb)</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><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">(lldb) n</span><br><span class="line"><span class="built_in">Process</span> <span class="number">1465</span> stopped</span><br><span class="line">* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over</span><br><span class="line"> frame #<span class="number">0</span>: <span class="number">0x0000000100fe5f2c</span> dyld`ImageLoaderMachOCompressed::doBindFastLazySymbol(<span class="keyword">unsigned</span> <span class="keyword">int</span>, ImageLoader::LinkContext <span class="keyword">const</span>&, <span class="keyword">void</span> (*)(), <span class="keyword">void</span> (*)()) + <span class="number">328</span></span><br><span class="line">dyld`ImageLoaderMachOCompressed::doBindFastLazySymbol:</span><br><span class="line">-> <span class="number">0x100fe5f2c</span> <+<span class="number">328</span>>: ldrb w8, [sp, #<span class="number">0x27</span>]</span><br><span class="line"> <span class="number">0x100fe5f30</span> <+<span class="number">332</span>>: ldrb w9, [x21, #<span class="number">0x139</span>]</span><br><span class="line"> <span class="number">0x100fe5f34</span> <+<span class="number">336</span>>: orr w8, w8, w9</span><br><span class="line"> <span class="number">0x100fe5f38</span> <+<span class="number">340</span>>: cbz w8, <span class="number">0x100fe5e4c</span> ; <+<span class="number">104</span>></span><br><span class="line">Target <span class="number">0</span>: (main) stopped.</span><br><span class="line">(lldb) x/gx <span class="number">0x0000000100fb8020</span></span><br><span class="line"><span class="number">0x100fb8020</span>: <span class="number">0x00000001d860e14c</span></span><br><span class="line">(lldb) <span class="built_in">image</span> lookup -va <span class="number">0x00000001d860e14c</span></span><br><span class="line"> Address: libsystem_c.dylib[<span class="number">0x00000001809a614c</span>] (libsystem_c.dylib.__TEXT.__text + <span class="number">263364</span>)</span><br><span class="line"> Summary: libsystem_c.dylib`<span class="built_in">printf</span></span><br><span class="line"> Module: file = <span class="string">"/Users/muhe/Library/Developer/Xcode/iOS DeviceSupport/12.2 (16E227)/Symbols/usr/lib/system/libsystem_c.dylib"</span>, arch = <span class="string">"arm64"</span></span><br><span class="line"> Symbol: id = {<span class="number">0x00000617</span>}, range = [<span class="number">0x00000001d860e14c</span><span class="number">-0x00000001d860e1a8</span>), name=<span class="string">"printf"</span></span><br></pre></td></tr></table></figure><p>可以看到符号地址已经被写过去了(0x0000000100fb8020)</p><p>至此,符号绑定过程完成。</p><h1 id="reference"><a href="#reference" class="headerlink" title="reference"></a>reference</h1><p>《程序员的自我修养-链接、装载和库》</p><p><a href="https://juejin.cn/post/6844903912147795982">https://juejin.cn/post/6844903912147795982</a></p><p><a href="https://juejin.cn/post/6844903922654511112#heading-10">https://juejin.cn/post/6844903922654511112#heading-10</a></p><p><a href="https://bbs.pediy.com/thread-263907.htm">https://bbs.pediy.com/thread-263907.htm</a></p><p><a href="https://iosre.com/t/ios-12-4-killed-9/15633">https://iosre.com/t/ios-12-4-killed-9/15633</a></p>]]></content>
<summary type="html"><h1 id="ENV"><a href="#ENV" class="headerlink" title="ENV"></a>ENV</h1><p>macos11.4 + iphone6 iOS 12.2</p></summary>
<category term="iOS" scheme="https://o0xmuhe.github.io/categories/iOS/"/>
<category term="RE" scheme="https://o0xmuhe.github.io/tags/RE/"/>
<category term="iOS" scheme="https://o0xmuhe.github.io/tags/iOS/"/>
</entry>
<entry>
<title>ql query for CVE-2021-30660 XNU Kernel Memory Disclosure</title>
<link href="https://o0xmuhe.github.io/2021/07/11/ql-query-for-CVE-2021-30660-XNU-Kernel-Memory-Disclosure/"/>
<id>https://o0xmuhe.github.io/2021/07/11/ql-query-for-CVE-2021-30660-XNU-Kernel-Memory-Disclosure/</id>
<published>2021-07-11T08:22:50.000Z</published>
<updated>2021-07-11T08:29:13.743Z</updated>
<content type="html"><![CDATA[<p><a href="https://alexplaskett.github.io/CVE-2021-30660/">CVE-2021-30660 - XNU Kernel Memory Disclosure</a></p><a id="more"></a><h1 id="Vuln"><a href="#Vuln" class="headerlink" title="Vuln"></a>Vuln</h1><p><code>msgsz</code> 可控</p><p><code>msginfo.msgssz</code> 是 8</p><p>如果控制<code> msgsz</code> 不是 8的 整数倍,比如9,就会导致在第二次循环的时候 leak出来 7字节的内核数据。</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></pre></td><td class="code"><pre><span class="line">next = msghdr->msg_spot;</span><br><span class="line"> <span class="keyword">for</span> (len = <span class="number">0</span>; len < msgsz; len += msginfo.msgssz) {</span><br><span class="line"> <span class="keyword">size_t</span> tlen;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* compare input (size_t) value against restrict (int) value */</span></span><br><span class="line"> <span class="keyword">if</span> (msgsz > (<span class="keyword">size_t</span>)msginfo.msgssz) {</span><br><span class="line"> tlen = msginfo.msgssz;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> tlen = msgsz;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (next <= <span class="number">-1</span>) {</span><br><span class="line"> panic(<span class="string">"next too low #3"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (next >= msginfo.msgseg) {</span><br><span class="line"> panic(<span class="string">"next out of range #3"</span>);</span><br><span class="line"> }</span><br><span class="line"> SYSV_MSG_SUBSYS_UNLOCK();</span><br><span class="line"> eval = copyout(&msgpool[next * msginfo.msgssz],</span><br><span class="line"> user_msgp, tlen);</span><br><span class="line"> SYSV_MSG_SUBSYS_LOCK();</span><br><span class="line"> <span class="keyword">if</span> (eval != <span class="number">0</span>) {</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> MSG_DEBUG_OK</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"error (%d) copying out message segment\\n"</span>,</span><br><span class="line"> eval);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"> msg_freehdr(msghdr);</span><br><span class="line"> wakeup((<span class="keyword">caddr_t</span>)msqptr);</span><br><span class="line"> <span class="keyword">goto</span> msgrcvout;</span><br><span class="line"> }</span><br><span class="line"> user_msgp = user_msgp + tlen; <span class="comment">/* ptr math */</span></span><br><span class="line"> next = msgmaps[next].next;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h1 id="Patch"><a href="#Patch" class="headerlink" title="Patch"></a>Patch</h1><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (len = <span class="number">0</span>; len < msgsz; len += msginfo.msgssz) {</span><br><span class="line"> <span class="keyword">size_t</span> tlen;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * copy the full segment, or less if we're at the end</span></span><br><span class="line"><span class="comment"> * of the message</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> tlen = MIN(msgsz - len, (<span class="keyword">size_t</span>)msginfo.msgssz);</span><br><span class="line"> <span class="keyword">if</span> (next <= <span class="number">-1</span>) {</span><br><span class="line"> panic(<span class="string">"next too low #3"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (next >= msginfo.msgseg) {</span><br><span class="line"> panic(<span class="string">"next out of range #3"</span>);</span><br><span class="line"> }</span><br><span class="line"> SYSV_MSG_SUBSYS_UNLOCK();</span><br><span class="line"> eval = copyout(&msgpool[next * msginfo.msgssz],</span><br><span class="line"> user_msgp, tlen);</span><br></pre></td></tr></table></figure><p>补丁保证了,在非8 整数倍的时候,只拷贝剩余的长度的数据。</p><h1 id="CodeQL-query"><a href="#CodeQL-query" class="headerlink" title="CodeQL query"></a>CodeQL query</h1><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line">import cpp</span><br><span class="line">import semmle.code.cpp.dataflow.TaintTracking</span><br><span class="line">import DataFlow::PathGraph</span><br><span class="line"></span><br><span class="line">// 存在误报 IOKit</span><br><span class="line">predicate isSYSCall(Function f) {</span><br><span class="line"> exists(Macro m |</span><br><span class="line"> m.getName().toUpperCase().regexpMatch("SYS(.)*") and</span><br><span class="line"> m.getLocation().getFile().getBaseName() = "syscall.h" and </span><br><span class="line"> m.getName().indexOf(f.getName()) > 0</span><br><span class="line"> )</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="comment">syscall -> copyout</span></span><br><span class="line"><span class="comment">source : syscall fucntion 's params</span></span><br><span class="line"><span class="comment">sink : copyout 3rd param(size)</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line">class Config extends TaintTracking::Configuration {</span><br><span class="line"> Config() { this = "taint size to copy size" }</span><br><span class="line"></span><br><span class="line"> override predicate isSource(DataFlow::Node source) {</span><br><span class="line"> exists(LocalVariable lv, Function f |</span><br><span class="line"> isSYSCall(f) and</span><br><span class="line"> lv.getFunction() = f and</span><br><span class="line"> (</span><br><span class="line"> not source.asExpr().(Literal).isConstant()</span><br><span class="line"> ) and</span><br><span class="line"> lv.getAnAccess() = source.asExpr()</span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> override predicate isSink(DataFlow::Node sink) {</span><br><span class="line"> exists (FunctionCall fc | </span><br><span class="line"> fc.getTarget().getName() = "copyout" and</span><br><span class="line"> fc.getArgument(2) = sink.asExpr()</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">from Config cfg, DataFlow::PathNode source, DataFlow::PathNode sink</span><br><span class="line">where cfg.hasFlowPath(source, sink)</span><br><span class="line"><span class="keyword">select</span> <span class="keyword">source</span>, <span class="string">" to "</span>, sink, <span class="string">" in "</span>, source.getNode().getFunction().getName()</span><br></pre></td></tr></table></figure><p>有误报,但是够用了,替换成copyin,也可以看看其他的调用路径,不过笔者没发现什么有价值的东西 : (</p><h1 id="reference"><a href="#reference" class="headerlink" title="reference"></a>reference</h1><p>CVE-2021-30660 - XNU Kernel Memory Disclosure</p>]]></content>
<summary type="html"><p><a href="https://alexplaskett.github.io/CVE-2021-30660/">CVE-2021-30660 - XNU Kernel Memory Disclosure</a></p></summary>
<category term="XNU" scheme="https://o0xmuhe.github.io/categories/XNU/"/>
<category term="XNU" scheme="https://o0xmuhe.github.io/tags/XNU/"/>
<category term="CodeQL" scheme="https://o0xmuhe.github.io/tags/CodeQL/"/>
</entry>
<entry>
<title>iOS RE 4 beginners 1 - MachO && class-dump</title>
<link href="https://o0xmuhe.github.io/2021/07/11/iOS-RE-4-beginners-1/"/>
<id>https://o0xmuhe.github.io/2021/07/11/iOS-RE-4-beginners-1/</id>
<published>2021-07-11T06:48:13.000Z</published>
<updated>2024-01-25T14:22:45.992Z</updated>
<content type="html"><![CDATA[<h1 id="roadmap"><a href="#roadmap" class="headerlink" title="roadmap"></a>roadmap</h1><p>之前在 <a href="https://iosre.com/">iosre</a>看到一张比较系统的iOS逆向学习路线图,因为接触过一段时间macOS上服务的漏洞挖掘,所以对*OS安全还是挺有兴趣的,也一直想系统性地学习下iOS逆向,之前的一直不成体系,也很零碎,正好对着这个图重构下知识体系。</p><span id="more"></span><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/ios_re.png" alt="ios_re"></p><h1 id="macho-file-format"><a href="#macho-file-format" class="headerlink" title="macho file format"></a>macho file format</h1><p> 类似Windows/Linux平台逆向学习,首先要学习正向开发的基础知识,以及涉及的文件格式(指可执行文件):</p><ul><li>Windows - PE</li><li>Linux - ELF</li><li>*OS - MachO</li></ul><p>根据roadmap中的app分析流程,第一步就是“砸壳“,就是在根据文件格式做文章,因为macho文件是加密的,被加载到内存执行的时候才会解密,所以我们做静态分析,需要把内存中解密之后的可执行文件dump出来,并修复文件才可以拖入hopper/IDA正常分析。</p><h2 id="Overview"><a href="#Overview" class="headerlink" title="Overview"></a>Overview</h2><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/iosre1/macho_overview.png" alt="Untitled"></p><p>我感觉这些可执行文件大同小异的味道,基本都是文件头+各种节区。 在macOS上你可以使用:</p><ul><li>MachOView</li><li>MachOExplorer</li></ul><p>来查看一个macho文件的结构,推荐前者,后者不知道为什么总是卡卡的,而且很容易崩溃 :(</p><p> 总体上来看,macho文件格式可以看做:</p><ul><li><p>Header</p></li><li><p>Load Commands</p><ul><li>LC_SEGMENT<ul><li>TEXT</li><li>DATA</li><li>LINKEDIT</li></ul></li><li>LC_CODESIGNATURE</li><li>LC_DYLD_INFO_ONLY</li><li>LC_XXXX_DYLIB</li></ul></li><li><p>Data</p><ul><li>Segment(1-n)</li></ul></li></ul><h2 id="Header"><a href="#Header" class="headerlink" title="Header"></a>Header</h2><p>只关注几个基本字段</p><ul><li>magic number : 表示macho的类型,FAT, ARMv7,ARM64,x86_64<ul><li>FAT 就是 “胖文件”,表示这个文件里包含了多个架构的MachO文件,可以使用<code>lipo</code>分离</li></ul></li><li>CPU Type, CPU SubType : arch</li><li>Number of load commands : Load commands的数量</li><li>flags:表示一些标识位,比如是否开了PIE,checksec可以从这里获取一些信息。</li><li>reversed:64位保留字段</li></ul><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/CleanShot%202021-07-11%20at%[email protected]" alt="CleanShot 2021-07-11 at 15.09.41@2x"></p><h2 id="Load-Commands"><a href="#Load-Commands" class="headerlink" title="Load Commands"></a>Load Commands</h2><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/CleanShot%202021-07-11%20at%[email protected]" alt="CleanShot 2021-07-11 at 15.17.40@2x"></p><p>即告诉操作系统,该如何加载文件中的数据。</p><ul><li><strong>LC_SEGMENT_64</strong>:定义一个段,加载后被映射到内存中,包括里面的节。 比如代码段 数据段 :<ul><li>TEXT 代码段</li><li>DATA 数据段</li></ul></li><li>LC_DYLD_INFO_ONLY:记录了有关链接的重要信息,包括在_LINKEDIT中动态链接 相关信息的具体偏移和大小。ONLY表示这个加载指令是程序运行所必需的,如果旧的 链接器无法识别它,程序就会出错。</li><li><strong>LC_SYMTAB</strong>:为文件定义符号表和字符串表,在链接文件时被链接器使用,同时也用于调试器映射符号到源文件。符号表定义的本地符号仅用于调试,而已定义和未定义的external符号被链接器使用。</li><li>LC_DYSYMTAB:将符号表中给出符号的额外符号信息提供给动态链接器。</li><li><strong>LC_LOAD_DYLINKER</strong>:默认的加载器路径。 <code>/usr/lib/dyld</code></li><li>LC_UUID:用于标识MachO文件的ID,也用于崩溃堆栈和符号文件的对应解析。</li><li>LC_VERSION_MIN_IPHONEOS:系统要求的最低版本。</li><li>LC_SOURCE_VERSION:构建二进制文件的源代码版本号。</li><li>LC_MAIN:程序的入口。dyld获取该地址,然后跳转到该处执行。</li><li><strong>LC_ENCRYPTION_INFO_64</strong>:文件是否加密的标志,加密内容的偏移和大小。<ul><li>lldb dump 砸壳修复文件之后,需要修改该标识位以确保正常反汇编文件。</li></ul></li><li>LC_LOAD_DYLIB:依赖的动态库,包括动态库名称、当前版本号、兼容版本号。<ul><li>“otool -L xxx”命令查看</li></ul></li><li>LC_RPATH: Runpath Search Paths, @rpath 搜索的路径。</li><li>LC_FUNCTION_STARTS:函数起始地址表,使调试器和其他程序能很容易地看到一个地址是否在函数内。</li><li>LC_DATA_IN_CODE:定义在代码段内的非指令的表。</li><li><strong>LC_CODE_SIGNATURE</strong>:代码签名信息。<ul><li>codesign -d [filename]</li></ul></li></ul><h2 id="Data-Segments"><a href="#Data-Segments" class="headerlink" title="Data-Segments"></a>Data-Segments</h2><p>各种节区,比如代码段,数据段,只读数据段等:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/CleanShot%202021-07-11%20at%[email protected]" alt="CleanShot 2021-07-11 at 15.19.59@2x"></p><p>这里可以看到很多<code>__DATA, __objc__?</code> 节区,<code>Symbol Table</code> <code>String Table</code>也单独列了出来。</p><ul><li>__objc_protolist</li><li>__objc_classlist</li><li>__objc_catlist section</li><li>…</li></ul><p>这些节区保存了OC中类名,函数名等信息,这就为从MachO中dump出来头文件打下了基础。</p><h2 id="Get-class-info-from-macho-file"><a href="#Get-class-info-from-macho-file" class="headerlink" title="Get class info from macho file"></a>Get class info from macho file</h2><p><code>__DATA, __objc_protolist</code>节区:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/CleanShot%202021-07-11%20at%[email protected]" alt="CleanShot 2021-07-11 at 15.30.45@2x"></p><p>存储的都是指针,指向一个又一个protocol的结构,可以参考objc的代码 : </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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">protocol_t</span> :</span> objc_object {</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">char</span> *mangledName;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">protocol_list_t</span> *<span class="title">protocols</span>;</span></span><br><span class="line"> <span class="keyword">method_list_t</span> *instanceMethods;</span><br><span class="line"> <span class="keyword">method_list_t</span> *classMethods;</span><br><span class="line"> <span class="keyword">method_list_t</span> *optionalInstanceMethods;</span><br><span class="line"> <span class="keyword">method_list_t</span> *optionalClassMethods;</span><br><span class="line"> <span class="keyword">property_list_t</span> *instanceProperties;</span><br><span class="line"> <span class="keyword">uint32_t</span> size; <span class="comment">// sizeof(protocol_t)</span></span><br><span class="line"> <span class="keyword">uint32_t</span> flags;</span><br><span class="line"> <span class="comment">// Fields below this point are not always present on disk.</span></span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">char</span> **_extendedMethodTypes;</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="class"><span class="keyword">struct</span> <span class="title">objc_object</span> {</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="keyword">isa_t</span> isa;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> ...</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>所以我们可以按照结构体索引 <code>__DATA, __objc_protolist</code> 里指针指向的位置的数据,就可以解析出来protocol的类型,名字,方法等信息。</p><h1 id="class-dump-read-notes"><a href="#class-dump-read-notes" class="headerlink" title="class-dump read notes"></a>class-dump read notes</h1><h2 id="env"><a href="#env" class="headerlink" title="env"></a>env</h2><p>macos11.4 + xcode12</p><h2 id="compile"><a href="#compile" class="headerlink" title="compile"></a>compile</h2><p>Q : <code>openssl/aes.h</code> not found</p><p>A : add header file path</p><figure class="highlight java"><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">export LDFLAGS=<span class="string">"-L/usr/local/opt/openssl/lib"</span></span><br><span class="line">export CPPFLAGS=<span class="string">"-I/usr/local/opt/openssl/include"</span></span><br></pre></td></tr></table></figure><p>XCode中的配置是:</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/111.png" alt="111"></p><p>Q : Library not found for -lcrypto</p><p>A : add the missing dylib</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/fix_crypto.png" alt="fix_crypto"></p><h2 id="raed-amp-amp-debug"><a href="#raed-amp-amp-debug" class="headerlink" title="raed && debug"></a>raed && debug</h2><p>核心逻辑就看</p><figure class="highlight objectivec"><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></pre></td><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)processObjectiveCData;</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">for</span> (CDMachOFile *machOFile <span class="keyword">in</span> <span class="keyword">self</span>.machOFiles) {</span><br><span class="line"> CDObjectiveCProcessor *processor = [[[machOFile processorClass] alloc] initWithMachOFile:machOFile];</span><br><span class="line"> [processor process];</span><br><span class="line"> [_objcProcessors addObject:processor];</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">- (<span class="keyword">void</span>)process;</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">self</span>.machOFile.isEncrypted == <span class="literal">NO</span> && <span class="keyword">self</span>.machOFile.canDecryptAllSegments) {</span><br><span class="line"> [<span class="keyword">self</span>.machOFile.symbolTable loadSymbols];</span><br><span class="line"> [<span class="keyword">self</span>.machOFile.dynamicSymbolTable loadSymbols];</span><br><span class="line"></span><br><span class="line"> [<span class="keyword">self</span> loadProtocols];</span><br><span class="line"> [<span class="keyword">self</span>.protocolUniquer createUniquedProtocols];</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Load classes before categories, so we can get a dictionary of classes by address.</span></span><br><span class="line"> [<span class="keyword">self</span> loadClasses];</span><br><span class="line"> [<span class="keyword">self</span> loadCategories];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="1-symbolTable-loadSymbols"><a href="#1-symbolTable-loadSymbols" class="headerlink" title="1. symbolTable loadSymbols"></a>1. symbolTable loadSymbols</h2><p>Load Commands 里找到 LC_SYMTAB,然后找到 __DATA(依赖属性 RW)。</p><p>然后利用 LC_SYMTAB 初始化了cursor开始遍历找符号。</p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/CleanShot%202021-07-11%20at%[email protected]" alt="CleanShot 2021-07-11 at 15.47.33@2x"></p><p>strtab 从 string table 开始 : 一个 symbol起始位置,一个string起始位置。</p><p>然后根据 arm 还是 x64 走不同的逻辑(这里目标是ARM64的Binary) : </p><p><img src="https://my-own-image.oss-cn-beijing.aliyuncs.com/img/CleanShot%202021-07-11%20at%[email protected]" alt="CleanShot 2021-07-11 at 15.48.18@2x"></p><p>开始解析 symbol table,item by item</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">string table index --> 在string table里找到对应的 string</span><br><span class="line">type</span><br><span class="line">section index</span><br><span class="line">desc</span><br><span class="line">value</span><br></pre></td></tr></table></figure><p>然后根据string table index里找到对应的string,放到symbols数组里,</p><p>根据 string 的 value 判断是不是 class,这里是根据字符串的开头是不是<code> @"*OBJC_CLASS*$_"</code>。</p><p>对于解析出来class name,添加到 class symbols dict里,<strong>这样处理之后,symbols, classSymbols都有了。</strong></p><h2 id="2-dynamicSymbolTable-loadsymbols"><a href="#2-dynamicSymbolTable-loadsymbols" class="headerlink" title="2. dynamicSymbolTable loadsymbols"></a>2. dynamicSymbolTable loadsymbols</h2><p>类似1</p><h2 id="3-loadProtocols"><a href="#3-loadProtocols" class="headerlink" title="3. loadProtocols"></a>3. loadProtocols</h2><p>从 <code>__DATA , __objc_protolist</code> 读取 对应的value</p><p>比如得到地址0x1009ccc58</p><p>走到 <code>- (CDOCProtocol *)protocolAtAddress:(uint64_t)address</code></p><p>初始化对应的<code>CDOCProtocol</code>对象</p><p>依赖这个地址,从文件对应地址读取出来 这个 <code>proto</code>的相关信息:</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">struct cd_objc2_protocol objc2Protocol;</span><br><span class="line">objc2Protocol.isa = [cursor readPtr];</span><br><span class="line">objc2Protocol.name = [cursor readPtr];</span><br><span class="line">objc2Protocol.protocols = [cursor readPtr];</span><br><span class="line">objc2Protocol.instanceMethods = [cursor readPtr];</span><br><span class="line">objc2Protocol.classMethods = [cursor readPtr];</span><br><span class="line">objc2Protocol.optionalInstanceMethods = [cursor readPtr];</span><br><span class="line">objc2Protocol.optionalClassMethods = [cursor readPtr];</span><br><span class="line">objc2Protocol.instanceProperties = [cursor readPtr];</span><br><span class="line">objc2Protocol.size = [cursor readInt32];</span><br><span class="line">objc2Protocol.flags = [cursor readInt32];</span><br><span class="line">objc2Protocol.extendedMethodTypes = 0;</span><br></pre></td></tr></table></figure><p>name protocols这些字段是一个地址,指向对应的值(字符串/数组)</p><p>最后参照objc2Protocol的值,分别获取protocol 的 name, 各种methods,属性等,初始化了protocol对象</p><p>所以protocols就都处理出来了,最后得到了</p><p><code>_protocolsByAddress __NSDictionaryM * 6781 key/value pairs 0x0000000112f93820</code></p><h2 id="4-protocolUniquer-createUniquedProtocols"><a href="#4-protocolUniquer-createUniquedProtocols" class="headerlink" title="4. protocolUniquer createUniquedProtocols"></a>4. protocolUniquer createUniquedProtocols</h2><p>依赖3中找到的 <code>_protocolsByAddress</code></p><p>name -> protocol 对应关系的dict addr -> protocol 对应关系的dict</p><p>p1->protocols 里还有protocol,merge进来(adopted protocols)</p><ul><li><p>p1 : _name __NSCFString * @”AWEFriendsActivityWidgetConfigurationIntentHandling” 0x0000000112fbc710</p></li><li><p>p2 : _name NSTaggedPointerString * @”NSObject” 0x07518ee6ed78d7f9</p></li></ul><p>@interface AWEFriendsActivityWidgetConfigurationIntentHandling : NSObject { //blablabla… }</p><p>这种情况</p><h2 id="5-loadClasses"><a href="#5-loadClasses" class="headerlink" title="5. loadClasses"></a>5. loadClasses</h2><p>解析section :<code> __DATA __objc_classlist</code></p><p>和3类似的套路,先得到 一个 地址,然后根据地址,去文件中索引对应的结构:</p><p><code>CDOCClass *aClass = [self loadClassAtAddress:val]</code></p><p>只调试一次过程分析即可:<code> val uint64_t 4335166480 In [2]: hex(4335166480) Out[2]: '0x102656410'</code></p><p>这个0x102656410,使用machoview也能看到,调试+machoview对比看,更容易理解。</p><p><code>loadClassAtAddress</code>方法分析:</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">struct cd_objc2_class objc2Class;</span><br><span class="line">objc2Class.isa = [cursor readPtr];</span><br><span class="line">objc2Class.superclass = [cursor readPtr];</span><br><span class="line">objc2Class.cache = [cursor readPtr];</span><br><span class="line">objc2Class.vtable = [cursor readPtr];</span><br><span class="line">objc2Class.data = [cursor readPtr];</span><br><span class="line">objc2Class.reserved1 = [cursor readPtr];</span><br><span class="line">objc2Class.reserved2 = [cursor readPtr];</span><br><span class="line">objc2Class.reserved3 = [cursor readPtr];</span><br></pre></td></tr></table></figure><p>也是读取对应的class结构,这个过程其实很眼熟,如果读过iOS逆向的书,比如庆神的书,有一章介绍oc方法调用过程的,会把oc->cpp代码,那里面这个 oc object的结构分析的很清楚。</p><p>然后解析<code> class->data</code> 字段</p><figure class="highlight objc"><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">struct</span> cd_objc2_class_ro_t objc2ClassData;</span><br><span class="line">objc2ClassData.flags = [cursor readInt32];</span><br><span class="line">objc2ClassData.instanceStart = [cursor readInt32];</span><br><span class="line">objc2ClassData.instanceSize = [cursor readInt32];</span><br><span class="line"><span class="keyword">if</span> ([<span class="keyword">self</span>.machOFile uses64BitABI])</span><br><span class="line"> objc2ClassData.reserved = [cursor readInt32];</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"> objc2ClassData.reserved = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">objc2ClassData.ivarLayout = [cursor readPtr];</span><br><span class="line">objc2ClassData.name = [cursor readPtr];</span><br><span class="line">objc2ClassData.baseMethods = [cursor readPtr];</span><br><span class="line">objc2ClassData.baseProtocols = [cursor readPtr];</span><br><span class="line">objc2ClassData.ivars = [cursor readPtr];</span><br><span class="line">objc2ClassData.weakIvarLayout = [cursor readPtr];</span><br><span class="line">objc2ClassData.baseProperties = [cursor readPtr];</span><br></pre></td></tr></table></figure><p>然后得到class 的 name,methods,protocol, property信息 然后返回这个class</p><p>展开说下 获取 methods && property的时候</p><ul><li><code>(NSArray *)loadMethodsAtAddress:(uint64_t)address; { return [self loadMethodsAtAddress:address extendedMethodTypesCursor:nil]; }</code></li></ul><p><code>loadMethodsAtAddress :</code></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">objc2Method.name = [cursor readPtr];</span><br><span class="line">objc2Method.types = [cursor readPtr];</span><br><span class="line">objc2Method.imp = [cursor readPtr];</span><br><span class="line">NSString *name = [self.machOFile stringAtAddress:objc2Method.name];</span><br><span class="line">NSString *types = [self.machOFile stringAtAddress:objc2Method.types];</span><br></pre></td></tr></table></figure><p>一样的套路,都是解析出来对应的字段,然后按照这些字段读取信息<code>(string) CDOCMethod *method = [[CDOCMethod alloc] initWithName:name typeString:types address:objc2Method.imp]; [methods addObject:method]; </code>最后获得methods数组,给前面填充class的地方使用</p><p><code>loadIvarsAtAddress ,loadPropertiesAtAddress , loadMethodsOfMetaClassAtAddress</code> 同理</p><p>至此,class解析完毕</p><h2 id="6-loadCategories"><a href="#6-loadCategories" class="headerlink" title="6. loadCategories"></a>6. loadCategories</h2><p>关于Categories 可以看 <a href="https://zhuanlan.zhihu.com/p/24925196">https://zhuanlan.zhihu.com/p/24925196</a></p><p>处理<code> __DATA __objc_catlist section</code> : </p><ul><li>(CDOCCategory *)loadCategoryAtAddress:(uint64_t)address;</li></ul><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">struct cd_objc2_category objc2Category;</span><br><span class="line">objc2Category.name = [cursor readPtr];</span><br><span class="line">objc2Category.class = [cursor readPtr];</span><br><span class="line">objc2Category.instanceMethods = [cursor readPtr];</span><br><span class="line">objc2Category.classMethods = [cursor readPtr];</span><br><span class="line">objc2Category.protocols = [cursor readPtr];</span><br><span class="line">objc2Category.instanceProperties = [cursor readPtr];</span><br><span class="line">objc2Category.v7 = [cursor readPtr];</span><br><span class="line">objc2Category.v8 = [cursor readPtr];</span><br></pre></td></tr></table></figure><p>可以看到和对objc2Class的处理有点像,就是因为是category的原因,所以字段有不同, 简单的理解成 处理一种特殊的class,并且提取出相应的 methods 和 properties就行</p><p>至此整个 process函数的处理结束</p><h2 id="7-处理-or-输出"><a href="#7-处理-or-输出" class="headerlink" title="7. 处理 or 输出"></a>7. 处理 or 输出</h2><p>这部分主要是处理输出了,如果没什么参数就直接stdout输出,如果有指定文件目录,就遍历之前process得到的信息,写文件(.h)到指定的目录。</p><h1 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h1><p><a href="https://zhuanlan.zhihu.com/p/24925196">https://zhuanlan.zhihu.com/p/24925196</a></p><p><a href="https://en.wikipedia.org/wiki/Mach-O">https://en.wikipedia.org/wiki/Mach-O</a></p><p><a href="https://iosre.com/">https://iosre.com/</a></p><p><a href="https://evilpan.com/2020/09/06/macho-inside-out/">https://evilpan.com/2020/09/06/macho-inside-out/</a></p><p>iOS应用逆向与安全 (刘培庆著)</p>]]></content>
<summary type="html"><h1 id="roadmap"><a href="#roadmap" class="headerlink" title="roadmap"></a>roadmap</h1><p>之前在 <a href="https://iosre.com/">iosre</a>看到一张比较系统的iOS逆向学习路线图,因为接触过一段时间macOS上服务的漏洞挖掘,所以对*OS安全还是挺有兴趣的,也一直想系统性地学习下iOS逆向,之前的一直不成体系,也很零碎,正好对着这个图重构下知识体系。</p></summary>
<category term="iOS" scheme="https://o0xmuhe.github.io/categories/iOS/"/>
<category term="iOS" scheme="https://o0xmuhe.github.io/tags/iOS/"/>
<category term="逆向" scheme="https://o0xmuhe.github.io/tags/%E9%80%86%E5%90%91/"/>
</entry>
<entry>
<title>CodeQL JS/TS Journey</title>
<link href="https://o0xmuhe.github.io/2021/06/01/CodeQL-JS-TS-Journey/"/>
<id>https://o0xmuhe.github.io/2021/06/01/CodeQL-JS-TS-Journey/</id>
<published>2021-06-01T03:43:11.000Z</published>
<updated>2021-06-09T14:33:55.580Z</updated>
<content type="html"><![CDATA[<h1 id="关于"><a href="#关于" class="headerlink" title="关于"></a>关于</h1><p>之前做过的一些使用CodeQL对JS/TS项目做扫描的笔记。</p><a id="more"></a><h1 id="关于构建数据库过程"><a href="#关于构建数据库过程" class="headerlink" title="关于构建数据库过程"></a>关于构建数据库过程</h1><p>对于JS/TS的项目来说,CodeQL统一都是 <code>--language=javascript</code> 的参数处理的,而且它主要是扫描,解析,然后构建数据库,对于小项目直接默认参数应该是ok的:</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">codeql database create --language=javascript <your_prj></span><br><span class="line"><span class="comment"># codeql database bundle -o <your_prj_db>.zip <your_prj></span></span><br></pre></td></tr></table></figure><p>但是对于比较大型的项目来说,因为CodeQL是Java写的,所以可能会存在内存不足导致构建数据库失败的情况:</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">FATAL ERROR: Ineffective mark-compacts near heap <span class="built_in">limit</span> Allocation failed -</span><br><span class="line">JavaScript heap out of memory</span><br></pre></td></tr></table></figure><p>默认给的内存是<code>2400MB</code>,大项目必然不够啊,文件太多了。</p><p>找了一圈没有解决方案,索性直接掏出JD_GUI把它的jar包给反编译了,发现是通过环境变量控制的:</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">export</span> SEMMLE_TYPESCRIPT_RAM=8000</span><br></pre></td></tr></table></figure><p><strong>这个不是给JAVA的那个内存设置(<code>-J-Xmx1234M</code>)</strong></p><h1 id="Query"><a href="#Query" class="headerlink" title="Query"></a>Query</h1><h2 id="接口函数"><a href="#接口函数" class="headerlink" title="接口函数"></a>接口函数</h2><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">interface FooInterface{</span><br><span class="line">4//...</span><br><span class="line">}</span><br><span class="line">export interface outerApiConfig {</span><br><span class="line"></span><br><span class="line">4foo: (params: xxxxx) => Promise<{ // whatever ..}>;</span><br><span class="line">4</span><br><span class="line">4bar: (params: FooInterface) => Promise<{ // whatever..}>;</span><br><span class="line">4</span><br><span class="line">4// ...</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>拿这个Demo为例,很多接口函数统一导出,需要借助<code>InterfaceDeclaration</code> 来找,不过我的方法有点“笨”。</p><figure class="highlight sql"><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">import javascript</span><br><span class="line"></span><br><span class="line">predicate isOuterAPIs(Function f){</span><br><span class="line"> exists(InterfaceDeclaration apis |</span><br><span class="line"> apis.getIdentifier().toString() = "outerApiConfig" and</span><br><span class="line"> apis.getAMember().getName() = f.getName()</span><br><span class="line"> )</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">from Function f</span><br><span class="line">where isOuterAPIs(f)</span><br><span class="line"><span class="keyword">select</span> f.getName()</span><br></pre></td></tr></table></figure><p>我这里实现很粗暴,就是限制函数名(字符串值)和Interface里成员名字(字符串值)一致,就认为这个函数是导出接口中的函数。</p><h2 id="特定参数的处理"><a href="#特定参数的处理" class="headerlink" title="特定参数的处理"></a>特定参数的处理</h2><p>在我的需求中,我需要重点关注,参数中带有路径的函数,换言之就是需要识别出这么多接口函数中,参数带有<code>path</code>的情况,那么很直接的思路就是利用正则,但是在实际的场景下,你会发现代码真的写出了“花”,不是常规的query能覆盖的。</p><figure class="highlight typescript"><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">foo: <span class="function">(<span class="params">params: WTFParams</span>) =></span> <span class="built_in">Promise</span><....>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">bar: <span class="function">(<span class="params">params: { arg: <span class="built_in">string</span> }</span>) =></span> <span class="built_in">Promise</span><{ ...}>;</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">magic</span>(<span class="params">x</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="params">()</span>=></span>{</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> };</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li><p>参数是一个interface,你需要对这个interface再限制,即这个interface的成员是不是path</p></li><li><p>参数直接就是 {arg : string} 这类情况</p></li><li><p>奇怪的函数写法,函数体在return里</p></li></ul><figure class="highlight"><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">class PathParamInterfaceType extends InterfaceType{</span><br><span class="line"> PathParamInterfaceType(){</span><br><span class="line"> getInterface().getAMember().getName().toLowerCase().indexOf("path") > 0</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">predicate isParamPath(Function f){</span><br><span class="line"> (</span><br><span class="line"> f.getAParameter().getType() instanceof PathParamInterfaceType</span><br><span class="line"> or</span><br><span class="line"> f.getAParameter().getType().toString().toLowerCase().indexOf("path") > 0</span><br><span class="line"> ) or</span><br><span class="line"> (</span><br><span class="line"> f.getNumParameter() = 0</span><br><span class="line"> and</span><br><span class="line"> f.getAReturnStmt().getExpr().(Function).getAParameter().getType().toString().toLowerCase().indexOf("path") > 0</span><br><span class="line"> )</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="必须依赖TaintTracking吗"><a href="#必须依赖TaintTracking吗" class="headerlink" title="必须依赖TaintTracking吗"></a>必须依赖TaintTracking吗</h2><p>最后一个问题比较简单了,就是有了source,然后再找合适的sink,看有没有路径就行了;但是其实还有一种办法会来得更直接,就是利用传递闭包,但是会带来比较多的误报,好处是实现起来简单,想要排除误报,只需要增加限制即可,看具体需求吧,哪个方法合适用哪个。</p><p>CodeQL的JS/TS部分实现不如cpp多,所以有些predicate需要自己手动实现,比如用cpp做query可以:</p><figure class="highlight plain"><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">FunctionCall getFunctionToACall(FunctionCall fc){</span><br><span class="line"> result = fc.getBasicBlock().getEnclosingFunction().getACallToThisFunction()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">select</span><br><span class="line"> getFunctionToACall*(FunctionCall fc)</span><br></pre></td></tr></table></figure><p>但是JS/TS部分没有<code>getACallToThisFunction</code> ,根据原理,手动实现一个即可:</p><figure class="highlight plain"><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">CallExpr getACallToThisFunction(Function f){</span><br><span class="line"> exists( CallExpr c |</span><br><span class="line"> c.getCalleeName() = f.getName() and</span><br><span class="line"> result = c</span><br><span class="line"> )</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">CallExpr getFunctionToACall(CallExpr call){</span><br><span class="line"> result = getACallToThisFunction(call.getEnclosingFunction())</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>所以,如果想要查询foo函数的传递闭包,就可以:</p><figure class="highlight sql"><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">from CallExpr <span class="keyword">call</span></span><br><span class="line"><span class="keyword">where</span> call.getCalleeName() = <span class="string">"foo"</span></span><br><span class="line"><span class="keyword">select</span> getFunctionToACall*(<span class="keyword">call</span>)</span><br></pre></td></tr></table></figure><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://xz.aliyun.com/t/7482">https://xz.aliyun.com/t/7482</a></p><p><a href="https://securitylab.github.com/tools/codeql">CodeQL for research</a></p><p><a href="https://ctftime.org/writeup/22177">https://ctftime.org/writeup/22177</a></p><p><a href="https://kernelshaman.blogspot.com/2021/01/building-xnu-for-macos-big-sur-1101.html">https://kernelshaman.blogspot.com/2021/01/building-xnu-for-macos-big-sur-1101.html</a></p><p><a href="https://github.com/D4rkD0g/boringforever/blob/main/xnu/boringanalysis/codeql_xnu.md">https://github.com/D4rkD0g/boringforever/blob/main/xnu/boringanalysis/codeql_xnu.md</a></p><p><a href="https://codeql.github.com/docs/codeql-cli/">https://codeql.github.com/docs/codeql-cli/</a></p>]]></content>
<summary type="html"><h1 id="关于"><a href="#关于" class="headerlink" title="关于"></a>关于</h1><p>之前做过的一些使用CodeQL对JS/TS项目做扫描的笔记。</p></summary>
<category term="笔记" scheme="https://o0xmuhe.github.io/categories/%E7%AC%94%E8%AE%B0/"/>
<category term="CodeQL" scheme="https://o0xmuhe.github.io/tags/CodeQL/"/>
</entry>
</feed>