-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
4565 lines (4335 loc) · 452 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>CleanMyMac X破解初尝试</title>
<url>/post/16190/</url>
<content><![CDATA[<h1 id="缘起"><a href="#缘起" class="headerlink" title="缘起"></a>缘起</h1><p><code>CleanMyMac X</code>应该是每个Mac用户耳熟能详的电脑清洁软件,以下简称<code>CMM</code>。我几天前用的是<code>CMM 4.5.3</code>版本,是在xx破解网站上搜索的TNT版本,结果今天一打开CMM发现,已经有了4.6.0版本的更新。与其到xx破解网站上搜破解版,不如自己再动手破解一遍,于是就有了这篇文章。我真的……一开始我也没想到这次破解会这么顺……</p>
<h1 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h1><ul>
<li><code>CMM 4.6.0</code>一枚;</li>
<li><code>Hopper Disassembler v4</code>一枚。</li>
</ul>
<h1 id="破解"><a href="#破解" class="headerlink" title="破解"></a>破解</h1><h2 id="查找线索"><a href="#查找线索" class="headerlink" title="查找线索"></a>查找线索</h2><p>首先将CMM拖进Hopper。我先搜索了一波UI中的Strings。搜索无果后,尝试搜索<code>vip</code> <code>register</code> <code>activate</code>关键词,发现<code>activate</code>关键词有很多激活相关项。</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/3/15/hop-cmm-search-str.jpg" alt="搜索字符串"></p>
<p>我找到了四个方法,名为<code>isAppActivated</code>。我想,相比其他复杂的函数与方法,那是不是把这四个方法patch了,整个CMM就能用了呢?</p>
<p>然而我又发现了第一个方法<code> -[CMActivationManager isAppActivated]</code>的伪代码调用了一个函数<code>_mLyGsJNgru0iJKGfhK</code>,并将这个函数的返回值(是<code>int</code>类型)赋予给了<code>rax</code>,而这个函数的内部逻辑很复杂,很可能就是验证激活的过程。</p>
<h2 id="修改程序"><a href="#修改程序" class="headerlink" title="修改程序"></a>修改程序</h2><p>接着上一个步骤,双击进入<code>_mLyGsJNgru0iJKGfhK</code>,打开伪代码模式,看着一长串的代码别着急,从程序入口开始,按照程序的<code>goto</code>走,顺藤摸瓜,最后<code>return</code>了一个<code>rax</code>——那简单,跳到汇编模式,直接:</p>
<pre class="line-numbers language-asm" data-language="asm"><code class="language-asm">mov rax, 0x1
ret<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<p>再打开伪代码,你会发现这个方法的伪代码已经变成:</p>
<pre class="line-numbers language-objc" data-language="objc"><code class="language-objc">int _mLyGsJNgru0iJKGfhK(int arg0) {
return 0x1;
}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>
<p>这就对了!</p>
<h1 id="留下足迹"><a href="#留下足迹" class="headerlink" title="留下足迹"></a>留下足迹</h1><p>为什么要有这一步,在我的<a href="https://www.52pojie.cn/thread-1126808-1-1.html">CrossOver 19</a>破解文章里已经说明了。在CMM中,我们要达到这样的效果:</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/3/15/hop-cmm-mod-credits.png" alt="留下足迹"></p>
<blockquote>
<p>注:这个窗口可以在程序顶栏 CleanMyMac X > 关于CleanMyMac里找到。</p>
</blockquote>
<p>这段文字是一个<code>.rtf</code>,在<code>/Applications/CleanMyMac X.app/Contents/Resources/zh-Hans.lproj/Credits.rtf</code>。打开这个文件,在开头按格式添加上你的Credit,注意一点,这个文件开头的几个空行要保留,否则达不到CMM的滚动效果。</p>
<p>之后,就大功告成了!</p>
<h1 id="BUG"><a href="#BUG" class="headerlink" title="BUG"></a>BUG</h1><ol>
<li>使用时,应用缺少权限,即使已经在设置里设置了磁盘完全访问。</li>
</ol>
]]></content>
<categories>
<category>计算机</category>
</categories>
<tags>
<tag>反编译</tag>
</tags>
</entry>
<entry>
<title>Hopper一拳搞定Interface Inspector</title>
<url>/post/24364/</url>
<content><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>最近本打算搞定 <code>Downie 4</code>破解版,可谁知开发者用了<code>Swift</code>语言编写,导致我这个小白破解总是不得逞。忽然间听说了一个叫做<code>Interface Inspector</code>的软件,感觉还挺厉害的。接下来先给大家介绍一下(这是官方介绍,不是广告):</p>
<blockquote>
<p><code>Interface Inspector</code> 使您可以在运行时研究任何<code>macOS</code>应用程序的用户界面结构和属性。</p>
<p>在程序中使用<code>Interface Inspector</code>非常容易。 您可以通过使用<code>Interface Inspector</code>中的快捷键<code>⌘⌥A</code>来使用附加菜单,然后选择要附加到的应用程序。 然后,您可以检查视图属性,或尝试使用不同的值来约束和更多。</p>
<p>将<code>Interface Inspector</code>附加到应用程序后,您可以使用快捷键⇧⌘K通过选择模式轻松标识各个组件。 只需单击您感兴趣的组件,它就会在<code>Interface Inspector</code>中被选中,然后开始分析其属性。</p>
</blockquote>
<p>总结来说<code>Interface Inspector</code>就相当于<code>iOS</code>越狱里的<code>FLEX</code>,或者相当<code>macOS</code>下的<code>Reveal</code>。不过后两者只能用于<code>iOS</code>,而<code>Interface Inspector</code>用于<code>macOS</code>。</p>
<p>这个软件的<strong>最新版本</strong>开发要追溯到2014年,算是一个很久没有更新的软件(估计以后也不会更新),不过仍然是付费的,所以<code>macOS</code>的 crakers,这一次我带你们 patch 它!你们有福利了。</p>
<h1 id="破解过程"><a href="#破解过程" class="headerlink" title="破解过程"></a>破解过程</h1><p>这次破解,我们的目标是:随便输入注册信息,即可使用App。</p>
<h2 id="收集信息"><a href="#收集信息" class="headerlink" title="收集信息"></a>收集信息</h2><p>这个软件的最后更新是2014年——如果你懂一些历史的话,你会知道那时候 Apple 的SDK非常落后,开发者的反破解方案很简陋,因此这个软件应该比较好破解。首先我们在注册 License 界面的UI中收集一些字符串(图一是要 License 主页面,图二是输错 License 的页面):</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/3/27/hop-ii-ui1.png" alt="license 主页面"></p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/3/27/hop-ii-ui2.png" alt="license 错误界面"></p>
<p>我们得到的字符串:</p>
<ul>
<li>Enter your registration details below, exacly as you recieved in your confirmation email:</li>
<li>Invalid license code</li>
<li>Please go back and try again.</li>
</ul>
<blockquote>
<p>注:exacly 单词其实拼错了,应该是 exactly。不过,一切以程序中为准。</p>
</blockquote>
<h2 id="Hopper-分析"><a href="#Hopper-分析" class="headerlink" title="Hopper 分析"></a>Hopper 分析</h2><p>将程序二进制文件拖入 Hopper 分析,然后在<code>Str</code>一栏随便搜索一条我们得到的信息,比如第一条,之后不断按<code>X</code>,找到代码引用的源头:<code>-[SMEnterLicenseViewController loadView]</code>,地址为<code>0x10010ff3a</code>。我们再把<code>SMEnterLicenseViewController</code>作为关键词搜索。这个类就应该是图一的视图管理器。这时候,我们发现了一个很可疑的函数<code>register:</code>。</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/3/27/hop-ii-search-smelvc.png" alt="搜索SMVC"></p>
<p>我们在<code>register:</code>处下个断,然后运行,随便输入一个<code>Name</code>和<code>License</code>,按下<code>Register</code>键——断下来了!这就说明,<code>register:</code>函数里包含着<code>License</code>的验证流程。</p>
<p>打开伪代码模式:</p>
<pre class="line-numbers language-objective-c" data-language="objective-c"><code class="language-objective-c">/* @class SMEnterLicenseViewController */
-(void)register:(void *)arg2 {
rdx = arg2;
r15 = self;
[self commitEditing];
r14 = [[r15 licenseName] retain];
if ([r14 length] != 0x0) {
rbx = [[r15 licenseCode] retain];
r12 = [rbx length];
[rbx release];
[r14 release];
if (r12 != 0x0) {
r14 = [[r15 delegate] retain];
r12 = [[r15 licenseName] retain];
rbx = [[r15 licenseCode] retain];
[r14 enterLicenseViewControllerDidSelectRegister:r15 withLicenseName:r12 code:rbx];
rdi = rbx;
[rdi release];
[r12 release];
[r14 release];
}
}
else {
[r14 release];
}
return;
}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>可以看见,第16行调用了方法<code>enterLicenseViewControllerDidSelectRegister:withLicenseName:code:</code>。我们双击看看这个方法:</p>
<pre class="line-numbers language-objective-c" data-language="objective-c"><code class="language-objective-c">/* @class SMLicenseWindowController */
-(void)enterLicenseViewControllerDidSelectRegister:(void *)arg2 withLicenseName:(void *)arg3 code:(void *)arg4 {
r8 = arg4;
var_30 = self;
r12 = [arg3 retain];
r13 = [r8 retain];
rax = [SMLicenseManager sharedInstance];
rax = [rax retain];
var_38 = rax;
rcx = r13;
rbx = [rax registerLicenseWithName:r12 code:rcx];
[r13 release];
[r12 release];
if (rbx != 0x0) {
[var_30 loadView:0x2, rcx, r8];
r14 = [[var_30 delegate] retain];
[r14 licenseWindowControllerDidRegister:var_30, rcx, r8];
[r14 release];
}
else {
[var_30 loadView:0x1];
}
[var_38 release];
return;
}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>我们仔细品这段代码。第11行<code>rbx</code>右调用了另一个函数,不过不要紧,<code>rbx</code>应该是一个布尔值。因为第14行的判断说明,如果<code>rbx</code>为真,那么就会执行<code>if</code>代码。你再看看<code>if</code>里的第17行代码里调用了<code>licenseWindowControllerDidRegister</code>方法,从字面来看有“注册成功”的意思,因此<code>if</code>里的代码是我们想要执行的,而不是<code>else</code>里的代码。</p>
<p>上面一自然段完全是我的逻辑判断。如果你觉得我说得啰嗦,你可以通过<strong>下断点</strong>来一步步判断代码到底执行了<code>if</code>,还是<code>else</code>。</p>
<h2 id="修改-License-验证"><a href="#修改-License-验证" class="headerlink" title="修改 License 验证"></a>修改 License 验证</h2><p>想必这一步在坐各位都会了吧!打开<code>CFG Mode</code>,然后按照图中的方式 patch:</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/3/27/hop-ii-cfg-patch.png" alt="CFG Patch"></p>
<pre class="line-numbers language-asm" data-language="asm"><code class="language-asm">jne loc_10010d137<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p>我们导出二进制,然后打开应用,成……咦?</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/3/27/hop-ii-signerr.png" alt="签名错误"></p>
<h2 id="修复签名验证"><a href="#修复签名验证" class="headerlink" title="修复签名验证"></a>修复签名验证</h2><p>就如提示框说的一样,这个开发者也确实不是傻子,还知道验证个签名,防止程序被 Crack。不过别着急,先用提示框中的文字搜索一波 String:</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/3/27/hop-ii-search-sign.png" alt="搜索签名 string"></p>
<p>看到了吗?红框中的 String 就是提示框中的字符串。</p>
<blockquote>
<p>这里教大家一个小常识,开发软件中怎样不让别人在逆向中搜索你的 String:你可以将必要的提示框间接写在一个<code>.html</code>中,这样字符串就不会再程序里直接出现。</p>
</blockquote>
<p>之后,我们随便点开一个字符串,不断按<code>X</code>查找引用,终于在<code>applicationWillFinishLaunching:</code>启动函数中的<code>loc_100024851</code>找到。</p>
<pre class="line-numbers language-assembly" data-language="assembly"><code class="language-assembly">loc_100024851:
r14 = [[NSAlert alloc] init];
var_F8 = r14;
r13 = [[NSBundle mainBundle] retain];
rbx = [[r13 localizedStringForKey:@"Signature of the Interface Inspector is broken" value:@"" table:0x0] retain];
[r14 setMessageText:rbx];
[rbx release];
[r13 release];
r13 = [[NSBundle mainBundle] retain];
rbx = [[r13 localizedStringForKey:@"The application has an unepected signature and seems to be broken or modified. Please re-download the application." value:@"" table:0x0] retain];
[var_F8 setInformativeText:rbx];
rdi = rbx;
rbx = *_objc_release;
[rdi release];
[r13 release];
r12 = rbx;
[var_F8 setAlertStyle:0x2];
r13 = [[NSBundle mainBundle] retain];
rbx = [[r13 localizedStringForKey:@"Quit" value:@"" table:0x0] retain];
[[[var_F8 addButtonWithTitle:rbx] retain] release];
[rbx release];
[r13 release];
r13 = [[NSBundle mainBundle] retain];
rbx = [[r13 localizedStringForKey:@"Visit Website" value:@"" table:0x0] retain];
rdx = rbx;
[[[var_F8 addButtonWithTitle:rdx] retain] release];
[rbx release];
[r13 release];
if ([var_F8 runModal] == 0x3e8) {
[**_NSApp terminate:0x0];
}
else {
r14 = [[NSWorkspace sharedWorkspace] retain];
r13 = [[[[NSBundle mainBundle] retain] localizedStringForKey:@"http://www.interface-inspector.com" value:@"" table:0x0] retain];
rbx = [[NSURL URLWithString:r13, @"", 0x0] retain];
[r14 openURL:rbx, @"", 0x0];
(r12)(rbx, @selector(openURL:), rbx, @"", 0x0);
(r12)(r13, @selector(openURL:), rbx, @"", 0x0);
(r12)(rax, @selector(openURL:), rbx, @"", 0x0);
(r12)(r14, @selector(openURL:), rbx, @"", 0x0);
[**_NSApp terminate:0x0, @"", 0x0];
}
[var_F8 release];
goto loc_100024b88;<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>在<code>CFG Mode</code>里,流程如下:</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/3/27/hop-ii-signcfg.png" alt="签名CFG流程"></p>
<p>我们只需要把主程序的最后一行<code>jne</code>改为<code>je</code>即可,也就是说,如果签名没有修改,那么就会报错;如果签名被修改,反而不会报错。</p>
<p>将<code>000000010002447e</code>的代码修改为:</p>
<pre class="line-numbers language-assembly" data-language="assembly"><code class="language-assembly">je loc_100024851<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<h1 id="改头换面"><a href="#改头换面" class="headerlink" title="改头换面"></a>改头换面</h1><p>接下来,我们该修改一下程序的<code>About</code>资源了。在<code>/Applications/Interface Inspector.app/Contents/Resources/</code>目录下找到<code>Credits.rtf</code>,之后在上面加上你自己的 Credit,然后在应用程序顶栏<code>Interface Inspector</code>-><code>About Interface Inspector</code>,即可看到你的 Credit:</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/3/27/hop-ii-credits.png" alt="你的 credit"></p>
<p>哈哈!我把我的名字加上去了!</p>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>虽然这么一个简简单单的小程序,搞起来也不是多么容易。况且我第一次上手时,搜索关键词来来回回搞了很多次呢。就算我们这样破解下来,还有个小小的 bug:每次程序启动,你必须重新输入一遍 Name 和 License,只能算是不完美破解。但是搞下来还是很有收获的——毕竟这个软件本身也来之不易啊!</p>
]]></content>
<categories>
<category>计算机</category>
</categories>
<tags>
<tag>反编译</tag>
</tags>
</entry>
<entry>
<title>Hopper之CrossOver 19 破解</title>
<url>/post/16189/</url>
<content><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>这篇文章不仅放在了我的博客上,还是本人在<a href="https://www.52pojie.cn/thread-1126808-1-1.html">吾爱破解</a>上的第一个帖子。我在吾爱破解论坛上发布帖子后,又将语言表达顺了一遍,放在了我的博客上,文中的图片链接全部引用自吾爱破解,可以看到吾爱破解的水印。<strong>此篇文章未经允许,谢绝转载,并仅供学习研究。</strong></p>
<p>最近一直在鼓捣各种软件的逆向与反编译,又准备要在Mac上运行Windows程序,于是准备上手<code>CrossOver</code>。但是感觉在五花八门的破解网站上下载的东西不安全,求人不如求己,所以我就当练练手,自己破解试试看,而后就有了这篇文章。</p>
<h1 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h1><ul>
<li>从<a href="https://www.crossoverchina.com/">CrossOver 官网</a>上下载最新版的 CrossOver 19 。</li>
<li><a href="https://www.hopperapp.com/">Hopper Disassembler v4</a> 一枚。</li>
</ul>
<h1 id="开始破解"><a href="#开始破解" class="headerlink" title="开始破解"></a>开始破解</h1><h2 id="分析软件"><a href="#分析软件" class="headerlink" title="分析软件"></a>分析软件</h2><p>首先打开<code>CrossOver</code>,简单的看了一下,分析出几个信息:</p>
<ul>
<li><p>打开软件后弹出了一个要钱弹窗,弹窗里提示了试用剩余天数,并提示你购买软件或者进行使用;</p>
</li>
<li><p>要钱弹窗中有两个需要关注的按钮,一个是 <code>现在试用</code>,另一个是 <code>使用购买信息解锁</code> ;</p>
</li>
<li><p>按下 <code>现在试用</code> 按钮,可以直接跳转到App里,开始14天试用;</p>
</li>
<li><p>按下 <code>使用购买信息解锁</code> 按钮,可以进行邮箱/密码激活,或者使用邮箱+密码+验证码激活;</p>
</li>
<li><p>开始试用App后,在<code>CrossOver</code>里仍然可以进行App激活操作。</p>
</li>
<li><p>嘿嘿,CrossOver竟然没有反调试。</p>
<p>于是,我们就有如下的破解思路:</p>
</li>
<li><p>Patch剩余天数;</p>
</li>
<li><p>Patch使用购买信息解锁的验证流程。</p>
</li>
</ul>
<h2 id="修改程序"><a href="#修改程序" class="headerlink" title="修改程序"></a>修改程序</h2><h3 id="Patch剩余天数"><a href="#Patch剩余天数" class="headerlink" title="Patch剩余天数"></a>Patch剩余天数</h3><p>本篇文章选用第一种思路“Patch剩余天数”,因为第一种思路相对简单直接。我试过第二种思路,对新手来说相对复杂,我就不再阐述了。</p>
<p>打开 <code>Hopper</code>,将<code>CrossOver</code>拖入分析。本来想搜索UI中的字符串,字符串搜索无果后,根据“剩余天数”的英文“left”,搜索关键词:</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/3/27/hop-co-serach-left.png" alt="搜索left"></p>
<p>我们发现,第一个函数<code>-[CXApplication daysLeft]</code>,于是打开伪代码发现,这个函数返回的是一个<code>int</code>,极有可能是剩余的天数。我们可以尝试Patch这里,使得该函数永远返回十六进制<code>0x8ef8</code>。将汇编代码改为:</p>
<pre class="line-numbers language-asm" data-language="asm"><code class="language-asm">mov rax, 0x8ef8
ret<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<p>这里我们要注意一点:十六进制<code>0x8ef8</code>对应的是十进制的<code>36600</code>(天)。我设置成36600,只是为了好看——36600天就是100多年。事实上,这个数值可以设置为任意大于0的整数,别忘了这个数值已经被我们 patch 了,是不会变动的。接着,我们动态调试运行一下,成功:</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/3/27/hop-co-36600.png" alt="剩余天数破解成功"></p>
<h3 id="Patch-激活窗口"><a href="#Patch-激活窗口" class="headerlink" title="Patch 激活窗口"></a>Patch 激活窗口</h3><p>你肯定觉得这篇文章不可能这么快结束。接下来,我要让你认识到一点:在App里面,仍然有某些按钮可以进行激活操作。比如程序顶栏上的<code>CrossOver</code> > <code>解锁 CrossOver</code>按键,如下图:</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/3/27/hop-co-regwin.png" alt="某些解锁按钮仍然存在"></p>
<p>而这些按键恰恰是我们不需要的。为了App的简洁,我们现在要把App里所有激活相关的按键禁用。与其禁用所有按钮,不如把这些按钮弹出来的<strong>同一个</strong>激活窗口禁用掉。这怎么做到呢?其实非常简单。这里有个小技巧,在<code>macOS</code>里,弹出窗口必须通过<code>windowDidLoad:</code>函数,因此我们可以将<code>windowdidload</code>作为关键词搜索:</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/3/27/hop-co-search-windowdl.png" alt="windowdidload搜索"></p>
<p>这时我看中了<code>DemoRegisterController</code>,它应该是一个注册管理类,所以他的<code>windowDidLoad:</code>方法弹出来的窗口很有可能是那个激活窗口。我们再把<code>DemoRegisterController</code>作为关键词搜索,把这个类的相关函数(图中红框内的方法)都<code>ret</code>掉即可。</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/3/27/hop-co-drcret.png" alt="DemoRegisterController搜索"></p>
<p>指令为:</p>
<pre class="line-numbers language-asm" data-language="asm"><code class="language-asm">ret<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p>修改后,以调试的方式运行<code>CrossOver</code>,发现注册相关的按钮已经按不动了。成功!</p>
<h1 id="留下足迹"><a href="#留下足迹" class="headerlink" title="留下足迹"></a>留下足迹</h1><p>我之所以把这个步骤设置成一级内容,是因为它实际跟破解没有关系。通过这一步,我要告诉你,任何一位craker为了保证自己的破解作品不被盗用或广泛传播(真正的craker破解出来的东西不会随便发给别人),都会注明程序是自己破解的。就像这样:</p>
<img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/3/27/hop-co-credits.png" alt="留下足迹" style="zoom:50%;" />
<blockquote><span class="custom-blockquote-svg"><svg width="24" height="24" viewBox="0 0 24 24" fill="" xmlns="http://www.w3.org/2000/svg" data-reactroot="">
<path fill="" d="M22 12C22 6.5 17.5 2 12 2C6.5 2 2 6.5 2 12C2 17.5 6.5 22 12 22C13.8 22 15.5 21.5 17 20.6L22 22L20.7 17C21.5 15.5 22 13.8 22 12Z" undefined="1"></path>
<path fill="" d="M15.97 11.5H16.04C17.12 11.5 18 12.38 18 13.47V13.53C18 14.62 17.12 15.5 16.03 15.5H15.96C14.88 15.5 14 14.62 14 13.53V13.46C14 12.38 14.88 11.5 15.97 11.5Z" undefined="1"></path>
<path fill="" d="M7.97 11.5H8.04C9.12 11.5 10 12.38 10 13.47V13.53C10 14.62 9.12 15.5 8.03 15.5H7.97C6.88 15.5 6 14.62 6 13.53V13.46C6 12.38 6.88 11.5 7.97 11.5Z" undefined="1"></path>
<path stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" stroke="" d="M17 8.5C15.23 8.97 14.07 10.84 14.01 13.27C14 13.33 14 13.4 14 13.47V13.5"></path>
<path stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" stroke="" d="M9 8.5C7.23 8.97 6.07 10.84 6.01 13.27C6 13.33 6 13.4 6 13.47V13.5"></path>
<path stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" stroke="" d="M15.97 11.5H16.04C17.12 11.5 18 12.38 18 13.47V13.53C18 14.62 17.12 15.5 16.03 15.5H15.96C14.88 15.5 14 14.62 14 13.53V13.46C14 12.38 14.88 11.5 15.97 11.5Z"></path>
<path stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" stroke="" d="M7.97 11.5H8.04C9.12 11.5 10 12.38 10 13.47V13.53C10 14.62 9.12 15.5 8.03 15.5H7.97C6.88 15.5 6 14.62 6 13.53V13.46C6 12.38 6.88 11.5 7.97 11.5Z"></path>
</svg>
</span><p>注:此窗口可以在程序顶栏<code>CrossOver</code>><code>关于</code>中调出。</p></blockquote>
<p>这个怎么办到呢?其实,如果你的App有着跟上图类似的界面,那么这个界面中间的电子权利信息(也就是文字部分)通常会被储存为一个<code>.html</code>、<code>.txt</code>或<code>.rtf</code>文件。你可以根据一下规则尝试找到它:</p>
<ol>
<li><p>如果程序支持<strong>多语言</strong>,那么它<strong>有可能</strong>在程序资源的多语言文件夹里,也就是:xxx.app/Contents/Resources/xxx.lproj/xxx.xxx。</p>
</li>
<li><p>如果程序只支持<strong>单语言</strong>,那么它<strong>可能</strong>在:xxx.app/Contents/Resources/xxx.xxx。</p>
</li>
</ol>
<p>在这个App里,我们可以根据第一个规则(此App是多语言的),通过下图找到电子权利信息:</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/3/27/hop-co-creditspath.png" alt="电子权利信息路径"></p>
<p>找到后,用文本编辑打开,在文件开头加上一句类似下面的话:</p>
<blockquote><span class="custom-blockquote-svg"><svg width="24" height="24" viewBox="0 0 24 24" fill="" xmlns="http://www.w3.org/2000/svg" data-reactroot="">
<path fill="" d="M22 12C22 6.5 17.5 2 12 2C6.5 2 2 6.5 2 12C2 17.5 6.5 22 12 22C13.8 22 15.5 21.5 17 20.6L22 22L20.7 17C21.5 15.5 22 13.8 22 12Z" undefined="1"></path>
<path fill="" d="M15.97 11.5H16.04C17.12 11.5 18 12.38 18 13.47V13.53C18 14.62 17.12 15.5 16.03 15.5H15.96C14.88 15.5 14 14.62 14 13.53V13.46C14 12.38 14.88 11.5 15.97 11.5Z" undefined="1"></path>
<path fill="" d="M7.97 11.5H8.04C9.12 11.5 10 12.38 10 13.47V13.53C10 14.62 9.12 15.5 8.03 15.5H7.97C6.88 15.5 6 14.62 6 13.53V13.46C6 12.38 6.88 11.5 7.97 11.5Z" undefined="1"></path>
<path stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" stroke="" d="M17 8.5C15.23 8.97 14.07 10.84 14.01 13.27C14 13.33 14 13.4 14 13.47V13.5"></path>
<path stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" stroke="" d="M9 8.5C7.23 8.97 6.07 10.84 6.01 13.27C6 13.33 6 13.4 6 13.47V13.5"></path>
<path stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" stroke="" d="M15.97 11.5H16.04C17.12 11.5 18 12.38 18 13.47V13.53C18 14.62 17.12 15.5 16.03 15.5H15.96C14.88 15.5 14 14.62 14 13.53V13.46C14 12.38 14.88 11.5 15.97 11.5Z"></path>
<path stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" stroke="" d="M7.97 11.5H8.04C9.12 11.5 10 12.38 10 13.47V13.53C10 14.62 9.12 15.5 8.03 15.5H7.97C6.88 15.5 6 14.62 6 13.53V13.46C6 12.38 6.88 11.5 7.97 11.5Z"></path>
</svg>
</span><p>这个版本的 CrossOver 已经由xxx破解。你可以放心使用。注意,请勿广泛传播……</p></blockquote>
<p>保存文件,再打开<code>CrossOver</code>,终于破解完成!</p>
]]></content>
<categories>
<category>计算机</category>
</categories>
<tags>
<tag>反编译</tag>
</tags>
</entry>
<entry>
<title>Mac视频下载软件Downie分析与暴破</title>
<url>/post/5e66dfd3/</url>
<content><![CDATA[<p>Downie,全能的网络视频下载软件。在论坛中搜索Downie,一大把一大把的TNT破解出现在眼前。但是求人不如求己,因此本文主要介绍Downie暴破,技术含量不是很大,包含中变量的tweak,和动态库之间交互的分析。</p>
<h1 id="软件分析"><a href="#软件分析" class="headerlink" title="软件分析"></a>软件分析</h1><p>打开Downie,在一系列的初始化后,会弹出试用期限窗口,提醒试用倒计时(14天)。操作可知,在试用期限中,软件的所有功能都可以正常使用。点开菜单栏中的<code>Downie 4</code>,可以看到几个关于注册的按钮。<br><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2022/02/16/downie-1.png" alt="downie-1"><br>通过我们观察到的元素,可以分析出如下三种逆向方案:</p>
<ol>
<li><strong>分析算法写注册机</strong>:现在软件都是联网注册,分析算法没啥用;</li>
<li><strong>修改剩余天数为定值</strong>:分析非常简单;</li>
<li><strong>修改全局已注册变量</strong>:也很简单,需同时去除校对验证函数。</li>
</ol>
<p>本文介绍二三两种方法。</p>
<h1 id="代码分析"><a href="#代码分析" class="headerlink" title="代码分析"></a>代码分析</h1><p>把主程序二进制拖进Hopper进行分析。对于这种应用程序的启动弹窗来说,懂Apple开发的都知道,我们可以从macOS应用程序的生命周期入手。搜索<code>appDidFinishLaunching</code>可以找到如下符号:</p>
<pre class="line-numbers language-none"><code class="language-none">s6Downie13XUAppDelegateC29applicationDidFinishLaunchingyy10Foundation12NotificationVF<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p>该函数会在程序启动完成后被调用。我们在<code>loc_100037546</code>中可以找到一些有关license的代码:</p>
<pre class="line-numbers language-asm" data-language="asm"><code class="language-asm">call imp___stubs__$s9Licensing11CMLicensingCMa
mov r13, rax
call imp___stubs__$s9Licensing11CMLicensingC6sharedACvgZ
mov rbx, rax
mov rax, qword [_$s9Licensing11CMLicensingC8delegateAA0B8Delegate_pSgvpWvd_100118910]
mov qword [rbp+var_30], r14
mov r12, qword [rax]
lea r14, qword [rbx+r12]
lea rsi, qword [rbp+var_E8]
mov edx, 0x1
mov rdi, r14
xor ecx, ecx
call imp___stubs__swift_beginAccess
lea rax, qword [_$s6Downie13XUAppDelegateC9Licensing011CMLicensingC0AAWP]<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>随便找一个call跟踪进去,都可以看到外部代码,在注释中都可看到如下路径:</p>
<pre class="line-numbers language-asm" data-language="asm"><code class="language-asm">; in @executable_path/../Frameworks/Licensing.framework/Versions/A/Licensing<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p>我们发现,Downie对license的验证流程原来写到了Licensing附加库中。把它拖到Hopper中,转而分析Licensing!</p>
<h1 id="破解一:修改剩余天数"><a href="#破解一:修改剩余天数" class="headerlink" title="破解一:修改剩余天数"></a>破解一:修改剩余天数</h1><p>观察代码风格,很明显Licensing是Swift写的。搜索<code>days</code>这样的字眼,在搜索结果中找<code>getter</code>,很明显可以找到一个Swift整型变量:<code>s9Licensing11CMLicensingC21numberOfDaysRemainingSivg</code>(符号),返回一个<code>int</code>值。在汇编中,把返回寄存器<code>rax</code>改为定值。</p>
<pre class="line-numbers language-asm" data-language="asm"><code class="language-asm">mov rax, 0xff
ret<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2022/02/25/downie-3.png" alt="downie-3"><br>嘿嘿,剩余天数从14天一下飙升到255天。在系统设置里怎么改日期,都不会变!朋友们,破解已经成功!</p>
<p>其实有一个遗憾,每次启动都会弹出这个窗口。我总想把它去掉,但找了许多有关的函数和<code>windowDidLoad</code>方法,很奇怪都未能实现。</p>
<h1 id="破解二(完美):修改全局注册变量"><a href="#破解二(完美):修改全局注册变量" class="headerlink" title="破解二(完美):修改全局注册变量"></a>破解二(完美):修改全局注册变量</h1><p>抱着试试看的心态。我搜索了<code>islicensed</code>关键词,没想到还真有这么明显的匹配项:</p>
<pre class="line-numbers language-none"><code class="language-none">s9Licensing11CMLicensingC10isLicensedSbvg<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p>赶快把<code>rax</code>改成0x1,启动调试,仍然未注册(节省空间,此处不贴图)。这说明软件获取注册状态调用的不是这个函数。到底是哪个环节出了问题?瞟一眼伪代码:</p>
<pre class="line-numbers language-objc" data-language="objc"><code class="language-objc">int _$s9Licensing11CMLicensingC10isLicensedSbvg() {
[sub_11600() release];
[rdx release];
r14 = [rcx activated];
[rcx release];
rax = r14 != 0x0 ? 0x1 : 0x0;
return rax;
}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>莫名其妙发现了一个名为<code>activated</code>的selector,无交叉引用,无来源。很有可能是外部符号。看看Downie还有什么库:还有Paddle没用呢!</p>
<p>把Paddle拽到Hopper分析,搜索<code>activated</code>,得到结果:</p>
<pre class="line-numbers language-objc" data-language="objc"><code class="language-objc">/* @class PADProduct */
-(char)activated {
rax = [self licenseController];
rax = (*(*rax + 0x58))(rax);
return rax;
}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>很标准的类方法,返回<code>char</code>类型,就是它了。打开汇编,<code>rax</code>改为1:</p>
<pre class="line-numbers language-asm" data-language="asm"><code class="language-asm">mov rax, 1
ret<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2022/02/25/downie-4.png" alt="downie-4"><br>很可恶,Downie直接崩溃!但这是个好消息,证明了我们的tweak是成功的,只是被某个检验流程检测到了。别慌,接着找找同类的关键词:<br><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2022/02/25/downie-5.png" alt="downie-5"><br>开发者小心思真多!两个verify函数,第一个指向第二个,我们看看0x95c5的代码:</p>
<pre class="line-numbers language-objc" data-language="objc"><code class="language-objc">/* @class PADProduct */
-(void)verifyActivationDetailsWithCompletion:(void *)arg2 {
r13 = self;
r14 = [arg2 retain];
rax = [r13 licenseController];
if (((*(*rax + 0x50))(rax) == 0x0) && ([r13 activated] == 0x0)) {
r15 = [[PADErrorFactory errorWithCode:0xffffffffffffff94] retain];
if (r14 != 0x0) {
var_88 = *__NSConcreteStackBlock;
*(&var_88 + 0x8) = 0xffffffffc2000000;
*(&var_88 + 0x10) = ___52-[PADProduct verifyActivationDetailsWithCompletion:]_block_invoke;
*(&var_88 + 0x18) = ___block_descriptor_48_ea8_32bs40s_e5_v8?0l;
*(&var_88 + 0x20) = [r14 retain];
*(&var_88 + 0x28) = [r15 retain];
dispatch_async(*__dispatch_main_q, &var_88);
[*(&var_88 + 0x28) release];
[*(&var_88 + 0x20) release];
}
else {
[r13 sendErrorToPaddleDelegateIfResponsive:r15];
}
rdi = r15;
}
else {
rax = [NSDate date];
rax = [rax retain];
[r13 setLastVerifyDate:rax];
[rax release];
[r13 saveProductFile];
r12 = [r13 determineActivationService];
r15 = [r13 licenseController];
var_58 = *__NSConcreteStackBlock;
*(&var_58 + 0x8) = 0xffffffffc2000000;
*(&var_58 + 0x10) = ___52-[PADProduct verifyActivationDetailsWithCompletion:]_block_invoke.209;
*(&var_58 + 0x18) = ___block_descriptor_48_ea8_32s40bs_e28_v32?0q8q16"NSDictionary"24l;
*(&var_58 + 0x20) = r13;
*(&var_58 + 0x28) = [r14 retain];
(*(*r15 + 0x68))(r15, r12, &var_58);
rdi = var_30;
}
[rdi release];
[r14 release];
return;
}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>观察到<code>PADErrorFactory</code>类,检测到问题会调用这个类的函数。怪不得程序会崩溃!转到汇编,赶紧<code>ret</code>掉。</p>
<pre class="line-numbers language-asm" data-language="asm"><code class="language-asm">ret<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2022/02/25/downie-6.png" alt="downie-6"><br>这波破解顺理成章,基本没怎么调试,完美暴破!</p>
<p>从破解的过程,可以梳理出Downie的业务逻辑结构如下:<br><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2022/02/17/downie-2.png" alt="关系"></p>
<h1 id="禁用取消激活功能"><a href="#禁用取消激活功能" class="headerlink" title="禁用取消激活功能"></a>禁用取消激活功能</h1><p>破解完后我们发现一个小问题,即点击取消激活后Downie会先弹出对话框,确定后app崩溃。<br><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2022/02/26/downie-7.png" alt="取消激活"><br>与其分析算法不如直接去掉这个功能。我们大致可推测功能应该在Licensing中。搜索deactivate关键词可搜到好多函数,但我们选择下面这个:</p>
<pre class="line-numbers language-objc" data-language="objc"><code class="language-objc">void _$s9Licensing11CMLicensingC17deactivateLicenseyyF(int arg0, int arg1, int arg2, int arg3, int arg4) {
rdx = arg2;
[sub_11600() release];
[rdx release];
r14 = [arg3 activated];
[arg3 release];
if (r14 == 0x0) goto loc_10ba3;
loc_10830:
var_30 = [objc_allocWithZone(@class(NSAlert)) init];
if (*qword_28418 != 0xffffffffffffffff) {
swift_once(qword_28418, sub_a6b0, rdx);
}
sub_1ca70();
// ............<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>为啥选的是这个?因为这个函数里有<code>NSAlert</code>的初始化。如果想去掉,<code>ret</code>即可。</p>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>可以看到Downie的静态库还真不少。这次破解主要是以静态分析为主,但要调试的话,也没问题。我破解速度并不是很快,总是不断试错。本人逆向能力也需要提升啊。</p>
]]></content>
<categories>
<category>计算机</category>
</categories>
<tags>
<tag>反编译</tag>
</tags>
</entry>
<entry>
<title>PocketSphinx实现语音识别</title>
<url>/post/7ab4abc/</url>
<content><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>我最近一直在搞一个名叫 A.N.N.A. 的 <code>Python</code> 语音机器人项目(Anna 是什么单词的缩写我就不说了,自己猜去),需要用的强大的语音识别技术。翻看全网教程,没有一篇说的通的,要么就是教会如何购买百度AI会员,要么就是教如何翻墙使用谷歌语音……</p>
<p>我最近发现<code>PocketSphinx</code>语音识别很不错,但是全网还是没有哪篇文章会告诉你,如何从头到尾下载这个库,用这个库进行语音识别,<strong>并且不在电脑上留下任何垃圾</strong>,可以随时删除的安装方法。然后你又会抱怨<code>PocketSphinx</code>有多么垃圾。</p>
<p>在这篇文章里,我将告诉你如何安装<code>pocketsphinx</code>,并将其嵌入<code>speech_recognition</code>库,来更好地实现最终的语音转文字。</p>
<h1 id="电脑环境"><a href="#电脑环境" class="headerlink" title="电脑环境"></a>电脑环境</h1><ul>
<li>系统:<code>macOS Catalina</code> v10.15.3;</li>
<li>软件<ul>
<li><code>Xcode</code> v11(版本其实无所谓);</li>
<li><code>Homebrew</code> (安装命令:<code>/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"</code>)</li>
<li><code>Python 3</code></li>
</ul>
</li>
</ul>
<h1 id="安装PocketSphinx"><a href="#安装PocketSphinx" class="headerlink" title="安装PocketSphinx"></a>安装<code>PocketSphinx</code></h1><p>我们先来看一下<code>PocketSphinx</code>的依赖关系图:</p>
<pre class="line-numbers language-none"><code class="language-none">PocketSphinx
|
|-- SphinxBase
| |--libogg
| |--libvorbis
| |--flac
| |--libsamplerate
| `--libsndfile
`-- Swig
`--PCRE<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<ol>
<li><p>打开终端,运行如下<code>brew</code>命令:</p>
<pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ brew install pcre
$ brew install swig<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<p>其中,<code>swig</code>是<code>pocketsphinx</code>的依赖,<code>pcre</code>是<code>swig</code>的依赖。<code>swig</code>不需要自己编译,直接用<code>brew</code>安装即可。</p>
</li>
<li><p>再使用<code>brew</code>安装<code>SphinxBase</code>:</p>
<pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ brew install SphinxBase<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p>这个命令会将<code>SphinxBase</code>及其所有依赖安装到电脑上。现在,<code>brew list</code>应该如下:</p>
<pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">index_out_of_range@imac ~ % brew list
cmu-sphinxbase libogg libvorbis portaudio
flac libsamplerate [email protected] swig
ldid libsndfile pcre<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>
<blockquote>
<p>注:<code>portaudio</code>、<code>ldid</code>和<code>openssl</code>是我自己安装的,不用安装。</p>
</blockquote>
</li>
<li><p>克隆<code>PocketSphinx</code>仓库,我们要在之后的步骤手动安装仓库,别问我为什么:</p>
<pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">git clone [email protected]:cmusphinx/pocketsphinx.git
# 或者(推荐)
git clone --recursive https://github.com/cmusphinx/pocketsphinx.git
# cd 到仓库
cd pocketsphinx-python<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>注意:必须<code>clone</code>,直接下载仓库会有文件缺失,因为这个库引用了其它仓库代码。如果网速太慢,可以fq或者把仓库导入<code>gitee</code>。</p>
</li>
<li><p>==使用<code>vim</code>更改关键文件==(如果直接用<code>pip</code>安装,会有文件出错<a href="https://github.com/bambocher/pocketsphinx-python/issues/28">^1</a>):</p>
<pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">vim deps/sphinxbase/src/libsphinxad/ad_openal.c<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p>接下来用<code>vim</code>把</p>
<pre class="line-numbers language-objc" data-language="objc"><code class="language-objc">#include <al.h>
#include <alc.h><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<p>改为</p>
<pre class="line-numbers language-objc" data-language="objc"><code class="language-objc">#include <OpenAL/al.h>
#include <OpenAL/alc.h><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></li>
<li><p>使用<code>setup.py</code>安装(至关重要的一步,仔细看命令!):</p>
<pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">python3 setup.py install<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p>注意是<code>python3</code>而不是<code>python</code>。我一开始没加3,结果安装到<code>python2</code>系统目录去了。</p>
</li>
</ol>
<h2 id="测试安装"><a href="#测试安装" class="headerlink" title="测试安装"></a>测试安装</h2><p>使用如下命令测试<code>PocketSphinx</code>是否安装成功:</p>
<pre class="line-numbers language-python" data-language="python"><code class="language-python">index_out_of_range@imac ~ % python3
Python 3.8.2 (v3.8.2:7b3ab5921f, Feb 24 2020, 17:52:18)
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pocketsphinx
>>><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>如果像我一样<code>import</code>没有报错,就是安装成功了!</p>
<h1 id="语音识别"><a href="#语音识别" class="headerlink" title="语音识别"></a>语音识别</h1><h2 id="英文识别"><a href="#英文识别" class="headerlink" title="英文识别"></a>英文识别</h2><p>安装完<code>PocketSphinx</code>后,就进入到我们的第二个阶段,嵌入<code>speech_recognition</code>模块进行语音识别了。你可以将<code>speech_recognition</code>模块理解为众多语音识别SDK与API的一个高层封装模块。使用<code>pip install SpeechRecognition</code>安装即可。</p>
<p>接下来,新建一个<code>.py</code>文件,输入如下代码:</p>
<pre class="line-numbers language-python" data-language="python"><code class="language-python"># 注:安装时用的是 SpeechRecognition 这一名称,但是注意 import 时要用 speech_recognition 这一名称。
import speech_recognition as sr
r = sr.Recognizer()
mic = sr.Microphone()
def recognize(r, mic, lang="en-US"):
print("Please wait...", end="") # 在开始录音前等待
with mic as source:
r.adjust_for_ambient_noise(source)
backspace = "\r" * 14 # 退格字符
print(f'{backspace}Say something: ', end="")
audio = r.listen(source)
# 调用识别函数(这个函数只有安装了 PocketSphinx 才能调用),具体方法看官方文档
result = r.recognize_sphinx(audio, language=lang)
# 打印并返回结果
print(result)
return result
if __name__ == '__main__':
recognize(r, mic)<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>使用终端运行代码(我用<code>VSCode</code>不知为何打不开麦克风):程序首先会打印<code>Please wait...</code>,等待一会<code>Please wait...</code>会变成<code>Say something:</code>,之后说一句英文(比如<code>hello</code>),程序就会打印出<code>hello</code>。</p>
<p>但是问题来了,我们是中国人啊!于是你把<code>recognize(r, mic)</code>改成了<code>recognize(r, mic, lang="zh-CN")</code>,程序报错了。</p>
<h2 id="中文识别"><a href="#中文识别" class="headerlink" title="中文识别"></a>中文识别</h2><p>说白了,程序的报错原因就是你没有中文语言模型,<code>PocketSphinx</code>是默认只有英文的。接下来我们添加中文模型:</p>
<ol>
<li>到 SourceForge 的<a href="https://sourceforge.net/projects/cmusphinx/files/Acoustic%20and%20Language%20Models">这个项目</a>里,找到 Mandarin(普通话),点进去有个<code>tar</code>包,下载下来并解压。</li>
<li>将解压出的目录改名为<code>zh-CN</code>,拖动到<code>/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/speech_recognition/pocketsphinx-data/</code>目录下。</li>
<li>按图中方式,将<code>zh-CN</code>目录里的文件按<code>en-US</code>目录里文件的命名规律改名:<br><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed/2020/4/30/py-sphinx-chname.jpg" alt="改名"></li>
</ol>
<p>现在把程序里<code>recognize(r, mic)</code>改成<code>recognize(r, mic, lang="zh-CN")</code>,程序成功运行!不过通过实验发现,这个模型识别出来的中文貌似很垃圾……只能自己训练模型了……</p>
<h1 id="END"><a href="#END" class="headerlink" title="END"></a>END</h1>]]></content>
<categories>
<category>计算机</category>
</categories>
<tags>
<tag>Python</tag>
</tags>
</entry>
<entry>
<title>“木兰”辞</title>
<url>/post/37946/</url>
<content><![CDATA[<blockquote>
<p>针对此前刘雷团队对外宣布“自主研发的‘木兰’编程语言体系“,中科院计算技术研究所1月24日在官网发布相关调查与处理意见,称刘雷在宣传活动中存在虚假陈述行为,同时在对外宣传活动中存在对单位的欺瞒行为。对刘雷的处理包括取消五年内专业技术岗位晋升的申请资格、全所通报批评、岗位等级降低等。中科院计算所1月15日对外发布该所计算机体系结构国家重点实验室编译技术团队自主研发、面向新一代人工智能和物联网应用的“木兰”编程语言体系,同时推出“木兰”开源软件包,供全球用户免费下载获取。随后“木兰”被指出就是Python语言“换了外皮”。中科院计算所随即于1月19日宣布当事人停职检查,对该问题展开深入调查。中科院计算所的最终处理意见称,“木兰”团队主要负责人刘雷在宣传活动中存在虚假陈述行为,同时在对外宣传活动中存在对单位的欺瞒行为。“刘雷交给媒体的宣传材料,与提交给我所宣传主管的审批材料存在严重的不一致,属欺瞒行为。”“木兰”语言分为前后两个版本,团队定义了语言规范,自主开发了编译器、字节码规范和虛拟机,基于开源的Blockly开发了可视化编程环境,针对中小学青少年编程教育开展了一些深入的工作,工作较为系统完整。但向媒体宣称“完全自主设计”有夸大成分;宣称“木兰”是“完全自主设计、开发和实现的编程语言”属虚假陈述;声称“木兰定位为下一代重要应用物联网应用的开发语言”缺乏应用案例的支撑;声称“木兰”语言采用了弹性actor执行模型、利用数据稀疏性提升效率技术属虚假陈述。意见决定对刘雷做出如下处理:取消五年内专业技术岗位晋升的申请资格;取消三年内科研项目的申请资格;全所范围内通报批评;岗位等级从工程师一级降低为助理工程师一级。 </p>
</blockquote>
<h1 id="《木兰辞》改编"><a href="#《木兰辞》改编" class="headerlink" title="《木兰辞》改编"></a>《木兰辞》改编</h1><p>无声复无声,桌前看新闻。不闻打字声,惟闻吾叹息。</p>
<p>问吾何所思,问吾何所忆。吾亦无所思,吾亦无所忆。适才见文章,美国限AI,清单十二卷,卷卷有国名。科研无自主,编程无语言,愿为<code>Python</code>改,从此当原创。</p>
<p>东市下破解,西市换衣裳,南市请砖家,北市发布会。旦辞GitHub去,暮宿中科院,不闻国人求自强,但闻科研经费打水漂。旦见利润高,暮生欺人心,不见中科求自立,但见木兰日夜吞蛇蟒。</p>
<p>狂窜排行榜,狂夺下载量。全国一起用,人人称真香。开发生百bug,运维十年归。</p>
<p>归见负责人,负责坐明堂。下千万功夫,赏千万大洋。负责问所欲,木兰不用走闭源,愿驰千里足,送儿继续闯。</p>
<p>国家内外闻,上网相保存;各语闻妹来,各个皆惊奇;网友闻姊来,逆向霍霍向木兰。开它<code>UPX</code>,撞它脆外皮。脱它假函数,著它真外衣。当窗来解码,照妖镜检查。裸奔同行前,同行皆惊忙:号称自主研,不知木兰是<code>Python</code>,<code>Matlab</code>做计算,H5写UI,双语傍地走,安能辨我是雌雄!</p>
<h1 id="《木兰辞》原文"><a href="#《木兰辞》原文" class="headerlink" title="《木兰辞》原文"></a>《木兰辞》原文</h1><p>唧唧复唧唧,木兰当户织。不闻机杼声,惟闻女叹息。</p>
<p>问女何所思,问女何所忆。女亦无所思,女亦无所忆。昨夜见军帖,可汗大点兵,军书十二卷,卷卷有爷名。阿爷无大儿,木兰无长兄,愿为市鞍马,从此替爷征。</p>
<p>东市买骏马,西市买鞍鞯,南市买辔头,北市买长鞭。旦辞爷娘去,暮宿黄河边,不闻爷娘唤女声,但闻黄河流水鸣溅溅。旦辞黄河去,暮至黑山头,不闻爷娘唤女声,但闻燕山胡骑鸣啾啾。</p>
<p>万里赴戎机,关山度若飞。朔气传金柝,寒光照铁衣。将军百战死,壮士十年归。</p>
<p>归来见天子,天子坐明堂。策勋十二转,赏赐百千强。可汗问所欲,木兰不用尚书郎,愿驰千里足,送儿还故乡。</p>
<p>爷娘闻女来,出郭相扶将;阿姊闻妹来,当户理红妆;小弟闻姊来,磨刀霍霍向猪羊。开我东阁门,坐我西阁床,脱我战时袍,著我旧时裳。当窗理云鬓,对镜帖花黄。出门看火伴,火伴皆惊忙:同行十二年,不知木兰是女郎。</p>
<p>雄兔脚扑朔,雌兔眼迷离;双兔傍地走,安能辨我是雄雌?</p>
]]></content>
<categories>
<category>随笔</category>
</categories>
<tags>
<tag>诗歌</tag>
</tags>
</entry>
<entry>
<title>“鼹鼠夫妻”笑谈</title>
<url>/post/229054ec/</url>
<content><![CDATA[<p>同学送我一对小鼹鼠——一夫、一妻。妻子体型肥硕,性格活泼,凡事总愿意起主导;丈夫身材娇小,性格稳重,儒雅斯文,不争不抢,全由妻子任意折腾。</p>
<p>她听利我的声音肯定最先睁眼,身子舒展开来,两只透光的耳朵坚起,满腔鸡血,上窜下跳,滑稽可爱。</p>
<p>任凭她百般折腾,他也懒得睁眼瞥她一下,急得她不得不用肥硕溜圆的屁股,把他硬生生挤进一个角落,用利落的瓜子撩他的鼻孔。更有甚为,干脆把他存在身下,枕着他瘦弱的身体享受阳光浴。他的眼睛犹如一颗星星,在黑暗的夹缝中闪烁着,说不上来是真心懒情,还是无力反抗。她累了,在他眼前留下一坨粪便,他也无动于衷。</p>
<p>我一真在将他们分离开与任由事态发展下去之间徘徊。我真觉得他们像一对老夫妻,活灵活现。</p>
<p>终于有一天,我把她拎出去了。</p>
<p>我将他放在个纸盒里晾着,叫她面壁思过,狠刹她的凶悍作风。接着,我暗喜丈夫能好好享受难得的清静与安逸。</p>
<p>她的锐气制没了,他的淡定与斯文也削没了。他一改往日的作派,看着他的妻子被隔离出去,抓耳挠腿,隔笼相望,看在眼里,急在心上。</p>
<p>原来一心想帮助他的我,不懂鼹鼠的幸福。</p>
]]></content>
<categories>
<category>随笔</category>
</categories>
<tags>
<tag>记叙文</tag>
</tags>
</entry>
<entry>
<title>《草房子》读后感</title>
<url>/post/33945/</url>
<content><![CDATA[<p>在这几天看的书中,给我印象最深刻的就是《草房子》,令我忍不住想要写些我的感想。</p>
<p>故事讲述了主人公桑桑跟随来接管油麻地小学的父亲来到油麻地期间,令他刻骨铭心、终身难忘的小学六年生活。在这里,他明白了许多发人深省的哲理,亲眼目睹甚至是经历了一连串感人肺腑、撼动人心的故事,例如:他和纸月之间心心相印的感情;不幸的杜小康与厄运作斗争的悲凉与勇敢;男孩细马的善良内心和他对尊严的执着坚持;秦大奶奶在生命的最后一瞬间所闪耀的人性光辉。桑桑在死亡的体验中,对生命深切而唯美的领悟,大人们之间充满诗情画意的情感纠葛……这一切,都在桑桑幼小而简单纯真的心灵里印下了深刻的烙印。</p>
<p>这本书留给我印象最深的,并非是特定一个人物,也不是某件事,而是每一个孩子的正直性格。书里每一章里写到的人物,都有一段非凡的经历,都展示了他们善良的品质、童稚的内心、顽强的意念,就像纸月怕让奶奶操心,在自己受欺负时默默无闻;红门落寞后坚强地面对孤独、饥饿、寒冷、生病、狂风暴雨等种种困难的杜小康;开始不爱学习后来又浪子回头、撑起家庭的细马;包括得了病却坚持上学的桑桑……在他们的心中永远都装满了甜蜜美好的事物,就像是纯洁的天使,在油麻地这片宁静的天空尽情飞翔。</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed/2020/1/29/grass-house-thumb.jpg" alt="草房子"></p>
<p>我认为,每个人都要在心里保留一座草房子,一座充满善良、温暖的草房子,一座充满勇敢、坚强的草房子。《草房子》用这些最纯真的爱告诉我们:每个人的一生都不是一帆风顺的,都充满了酸甜苦辣,困难和幸福犹如白昼和黑夜一样,永远与我们相伴,<strong>当苦难来临的时候,我们不能逃避,要满怀希望,像油麻地的孩子们,微笑着去面对。</strong></p>
]]></content>
<categories>
<category>随笔</category>
</categories>
<tags>
<tag>读后感</tag>
</tags>
</entry>
<entry>
<title>个人介绍</title>
<url>/post/19608/</url>
<content><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="抱歉, 这个密码看着不太对, 请再试试." data-whm="抱歉, 这个文章不能被校验, 不过您还是能看看解密后的内容.">
<script id="hbeData" type="hbeData" data-hmacdigest="0e499d57839a4a17844da8c1fdddf066093f301a1e27ddb71d7601d967d3cda3">fb9485e9b3db22a0b31c2c272a92e2cc6a9ca25ea7de9761ab9b1e137bd75bcd2a9612d9cc31ad3d7c91d04f05d8475dedecb590bfdd1269d523d9e1b3398ae563e1161cee06e5245c06c82c56ad197241898f732d8868631aa2a01e103ce1efa0742854121c67465ba6ded7a85a4e5b6d9cae734c10c092b6b0513c72c55c33ea5b5c4cc005d8887532654d500e7c1222b134654c12099653eb2912ac7949d16387d53506c89f013062bee2278f5737d84ec646307b92f935d3f95d2d71ac3e96f06635474dbf36bc2ac9f9f61590fde241c25be1d48353d4f5a5f18db6974e9490d6118f439219c4abef10238a580a69dee8378786f70ad94167fb23efa45506ef449483822ca95cac85649cdeccf74bb5ffa2c32ec1bc0d0a626a44922622ba6dbe8340ad63ed4c0b894d4cb89a0941374d49383c9ca7ce8cef3e205262c30e83dcae4a94d64996546d9b31905dbc0940cdb61d6403081ec436db72bb471a9598f253f2cda0af848dedb2f8eb23bb98f43c7334aad2f9bcdfd8efdc753147426855d4e55682e59e8442cdbe6543737a7839f5ebc00a19735823c634692c3465a52a398c23009a2e841e04b3016e5cdeb572b67e02531e746c6240aa826f9130f6476a43a66f0670c0758883c3756b430c1cf9292fac33dc7638c92f6073a3719977628c7ebb67e75eee8b7e7f90e3e43ec1601a83ec4e443cf7923e49caf7ad996de23a1849ebeff570492398e18ae08abe31198bc1d334dd7b50100e99ffed67b94f86a67e497669e3cf25c73cc13c5c0609e41d15058955cc3a0215fe078553a26c0a7d068ca0f438865e12a31cd23f92809c7cf78345d5edd31088b3ec04b2f91bb7d043af8ffcaea4f0597ecd3999deeeff7065d547ece4e94eeb00be45ce83276b58eb24bda499d47e093e82c0f96973859ce11814fedc6655496191acf215f5256693521b53c3d695b24565e21b8f585fa241ee3c3bf1ecdb89443e1ee11bbd40031beeca22fcb1b26962ffdd9aacaa3eaade87f0e72eef2737f747bc2869f3dd61c4bb919975310a3ccab052cafa36fd9bce2d801ffc48a97efc1ec1ab28982e71b8ce7b27d835c4a23cd3d5a4842c5c515fc2470b51b9a2e39f62a3284e912908840c8aa0faa6b5fddea5442003599cf39f20dd4dffcb43ab26a6f84cebcd83db1322874792ed4abedfb2e321b2085324fc1fda61f0a7db9aedb832579c93d1fe745b71523d6dd870520168040edd7d578ef68fa30f892fe25898133eb4eaea44434882bb29fa8f60ce6a1a9e27676a6709454099d0c7c77a5fbc107670cdf28214aeb31bc4fce69f985fef7d3cf3dec620303d7d7562f20f6219ba53ae80deb323f29259e373d64058bebecc07907c2884f2346da79b238be730e7b3c0daa665261533d8d994ef986a8838c07f7a4fc6a09d0c84a4baac9127db62e5ed041f1d4803a3518cb860662c40d77a49be3dea9a0c285b29b8b18c1b9fa67367d56059efca93d34661207fce3e1eefa0795dcc0b396d20c77b41beaab96bbe7d51a465ed9e28d74c12e4de8ca62f227309f14fdaea84f4491c76186fce9f59959640e6a3cef6dc704a5eadfaaae19b1b5dbdb9c36b294bed237be2292cf7151e9c4c7b190ec49304aecae83cea9a9d5d9bbf7a37ef89aa408ba76a41f701960d1dbd17f3c2d9c9eb0ccef2be40db8adb5d8ccb6ebc295a45b5cedadd84e87bd0ee25de67da8e162f7ad67a1ce1fcbd0fdf594141af2f47eaf8e1b84729043e0df633a243d2b40ca3108ef524310b370e4ea4339999b7623b983bb62687555967df1e85d5db1b6d8729122ba89ebaade06c0092bd9196cee741d1c6be043920790815b6d98df760bd8dac22024e3ea1a72f347082f03a25842edfdec7cb74ffae81c1f21e585e41de8d4d09f6140d069fae298c7a6e1c73dce5fbc58fd8c79279e2c3db154f04907be67b521aed3b6422ec2c119d8b8e88218667453a7f0f088d38d2da49c63ae1db9655b12f1ca4d6b0e68c27ecedd8530309c641b5d85a8ab1117fa5d322b54a236a3ea23b36be512bfdc290b24b5a6a769e734a69cdd34e5979a8f7a2cacb100d33b28f27a4d0b9a981c75bd93391208a44bc5c148c84ec61b903a8db168823f0a79b08efed78a383d736180437d46869146729e302345732cba1e1f1773e88d6cb1929e9b1128c9a68d5bae647104f53dfe47fb99941afd72aea4bde959e1ee234f243cc75cace050d171560d44121b2430ed883256ffe817ca56ace18064e0bd7f0374e02349b24c7694777921fa4e36366b0f345d04274279e5a6d425011fc80872687cc1caa285f9b5d6403c2f57a878b7af33ba849b3f6f47f364cc4b68c8f5f8a10fbc07a2d957613d346a916f59e71dec428792fde1eb46aabe37c1357501bc0347d5315fbbf57cd7d083c5e659d098302f49e7c6771e93d3ef6109e32eaef731af357ed0324f2012702d8f99affe80f73e043841f6481a0f4392cad87f1f6de5828f7f1abfa02b2bff0dd2d396f17ec199c72565b6410600ca3954efffc7b787a36cc0f5868120b29468ac29d2981b9b50dc800b6df08bd653e9e6d1903f5e4052af95efb295d4a806ac6e6aeccd6f319f01852c0fd90cc2133755a7325f80d42365c8b8f73a5a1956f1e60281c6c35cc4b2f3d11cc8dac95e4581452831f5ac5eb978f15279cfe97966b3bf2e7937a4b3daf7f777c24672724610cba207afbda0c8d78541f214998f75cc9141ef09df63547e8e7f875a231182f539ddd1434f3aa8026ecc069942317985a475bd4dafe29f7eecd168c328d86fa223cc1be5500cefa623d6a8c94080729bd67556989b143e5c3af95645b35ca87aa45de7056185497de2665bcb98a8a98343e5a3d067b553d7459752dc05c1936ff0c128f2e9d844c5eb1f29c6c631c35605ad71596ce5b417ab61c6a5fcfdc97b101f21afd184b278a71c88cc0b6853b603e36c614c28e4a6d64ce15981ac24932e02425f080f3d4e9ad3d3f29fb889364ca0cd23fa27ba2cbb21d8be468fe6634f4fcaba34f25d215d44b4f5e15210c4375c5834fb7f461a15e07c333638dfea0e4217f91863c0453a6f1f3492a2bb6bfb8e6761a0dd1fbd311baeacc596c1316cf7313a58219788b9957857c144ac254aebc9b0e6c74f8b8badb323bab60cf3a1a638e514cb14830e31034586f017b27c757f4a25b5e306b81ef139baded210927439de88a32bdd043a1f05d262774d72945435dfe54aba30f48b9b8791ce186b16ff0bf6dbd8b66d1631a2a7a0c8521ec0a7797595267342f15af77a451f1a1df22f78ca5415c015e5bed0afc30b6b306f1e6952d251dc70c921e99e5b362dc84a1a7edaf255297cd6f834aedfe77d27cae694e831f6b2f57f4436e1e1ced8952d009d092de665dd90f8d7a3eae967c74c1f1b3533a89081a37d829131736c1d1692a2bc1206e022a3fb165eff13e99f4f6bbcb3c569911b858f98547ab97ba830666f1f421644477465020243f7bcbd4894f95e264bfb5137f55fc20b946df46d166e0e2b550a6ccdced3bcf3e74d23bb439c0f976ab20766689eab1ee86c498f17ca0a666bff50e1ca031001d6d98032942b64e5cee99756f0b7b25edc99641efb330b70cef13676a71112fcb55f6c40baa8c97ec89bdfeaaa68f7adc03cccf629e3156d0edaf32a5e4ae6f9586f30a25cec03276418952c6e5d14fc515f8287d91874dab54c3eea6ed96c18d2c64b5350aa62614f84c34f1fc117e55e5ec54b773174bf1693355fd1788927b3d05b2e80fcd90da95afb04981c87461ab80468957bdffec4b4007ffbbd20206ed02c8f4560ced7021facc4869ea91ccca4fd0dfa24c22577bcb5a2ef422cfc6fa3b333b9d03cd5ca1a13d7a0baf985d1784cf8892611b8ee4cc2aadaa30ea3496200a7569ed4aee3199c8821f05e558c8bdc3eba7775b3d05520f208f5da6e5dd5663c32618a723abccfeafdc5929af39bd367c06b7ee8c2be813afc282ede67a736a2e37875e3fe658e015f700e6d69dfb1de649b4a5fc8e5d59eef57ae29b764f5e7920d80df866f4bf8b789041c420f9ab2cdfb6ca91b81a10e6a70896e1f583a80c267ea5cc37e570584d8e9f91cace633e577710c01bcfc672285bb7ba98526d93261aa55d8da22ffefbf86935c4f8e4bb0d63a16a0354878b1adabaef21cc94c7792e3bfd9827930c49d82d2eba769a7e7a6df51a274f451b5a501ad7e57dd72ad3a38c14ce8288c9fcb81703ff8174b74a408205c0a88b70ed9646ab23f3c72a5b8b2cd7002a8b5e6885657b08295c9cfd829827b0bff7688b905e043b19ba3a8cefd25cfbafad5a8fe860d3799bd0aa829b770c15bcd2ef50a12922980ff97d7bac3ca452627b50f696636ba408e3fb766a8d4343ebb0b7e0343806296c7aaa035660a9110782869cb5299735ceaec41604c8d09bc13377b975a30899b18b643e7854e522998133af57dee4db3cd37b49a17bd1fca1b82d3f4dc99a896226ca502a0e1c35a852eecf946cade11525ba43cb3c63558a5c0ebae2f3d7d79a1f5ff12d2e4ffde9ef6f92c0b22a7da5ec045c1fe5a0e1998131ae9f23299b32a72fcac1874acec5bf88fffeba4c50078661177176074845591febdb26c71fcea4e2c699066947eb446e503ea83db995d9a47556ec5b5cae4a17d1455f215c51ff7036427ed2b0abdd62d3b7265f1f82b254da6127ac9c4d1e30bb9d60cc2cd06c3e6772e47d32188743998a4f65e33c4d8e1e553320dc5c672067e59512c2bd534d9127b918c27fa0fdda6bb46c63f14b825daf2e5073a25ad45f3c4307ecb1439767e27a39e64cc0d1641712bddc9c1014f352a0b3e886c5c463abdbf5ac9735f45ad3f6001fedaf575a0597f84c27c6b3b896cad4e04fa9c78cea1764a248d26d9530053ce004a1b4c582623f92d515e5fd4188a1d9bda32c4f64423ecf89ca0b42efe8d32c9656653963ab2e78790361d29ede8faf0c7fd679a1636def18059d96a1ce99c21bc834aa5760c22819238eac792a7c07d86a92c94ba735494f3a3c0896aec8180cbf070ab3efe8cc1a1467258a5cccada490df782e21b46205d205e6554c8bca440fdc0c5c69b84ed59547ccb346d523b8ae98533e3f6b05a84662a2ecc03bdeb70ff41ee56d09e3b25373bb002315f0acc94b4b8669b3c743f82a95054208</script>
<div class="hbe hbe-content">
<div class="hbe hbe-input hbe-input-default">
<input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass">
<label class="hbe hbe-input-label hbe-input-label-default" for="hbePass">
<span class="hbe hbe-input-label-content hbe-input-label-content-default">您好, 这里需要密码.</span>
</label>
</div>
</div>
</div>
<script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
<categories>
<category>随笔</category>
</categories>
<tags>
<tag>说明文</tag>
</tags>
</entry>
<entry>
<title>中秋月</title>
<url>/post/c25a8065/</url>
<content><![CDATA[<p>中秋节到了。一年中最圆的月,就在我眼前。它圆得硬朗、圆得结实,白得明亮、白得柔和。几丝灰色的瘢痕,淡去了冷清平白,增添了一分粗糙的触感。我紧紧贴着窗户,盯着,但它急着往高楼的后头躲——看不到,也就罢了。但是,脑海里闪现出刚刚去姥姥姥爷家串门的画面告诉我,这轮明月对长辈来说却无比重要。</p>
<p>到达姥爷姥姥家。推开门,脱掉鞋子,往旁边随意的一踢,却发现毯子上,三双拖鞋,整整齐齐,朝着大门的方向已经摆好。红的、蓝的、白的,三种颜色,三种大小,与我和父母脚完全吻合。升降椅一个、小方凳一只、纸箱子一个,无疑是为我们换鞋做准备。</p>
<p>我初来客厅。茶几上的水晶垫,光滑油亮,仍保留着抹布的水迹。桌子上的果盘,是用来“祭”三个神仙的:钢盆上是前几天妈妈送的西梅,十九颗黑宝石,冻出一层白色的冷霜;紧接着是黄色的浅盘,三个猕猴桃分半,新鲜的空气使它着凉,透出深入果肉的墨绿;最高级的陶瓷盘上,铺满刚切开的梨,有棱有角、方方正正,都是清一色的白翡翠,并无任何瑕疵;甚至成袋成袋的酸奶也要摆在盆里,温水嘟嘟吐着哈气,酸奶围成一圈躺在盆壁上,仿佛泡着温泉,整齐划一。水果刀、叉子、牙签,整齐地站在三个塑料小盒里,梨皮规整地倒入桌上的垃圾桶中,所有事物一应俱全。</p>
<p>月色入户,时间不早。鞋厅里我们的那盒芒果成了争吵的对象。“妈,那盒芒果你赶紧拿走吧!”“哎呀,你别拿那么多,我这里还有一堆呢,都快放坏了!”“这芒果都软了,盒底下潮着呢。”“那你让天来赶紧吃了啊!”“他和他爸都不吃,剩下的就光我一个人了……”姥爷二话不说,拎起芒果直接塞到妈妈的手里,触电一般,妈妈立刻将它塞回姥爷的手。古铜色的手、白皙一点的手、皱皱巴巴的手,拧在了一起,坚决要把礼品推让给另一方。无奈的声音、严厉的声音、坚决的声音,互相纠缠着,誓不让步。</p>
<p>我只好孤零零地坐在一旁,看着天边最圆、最亮的月亮,忽然明白了姥姥姥爷在月圆之夜的期盼与希望:长辈一年到头,就指望着这一轮明月、一缕月光,在他们的眼里,别无他物,只有对儿女最真切的爱。</p>
]]></content>
<categories>
<category>随笔</category>
</categories>
<tags>
<tag>记叙文</tag>
</tags>
</entry>
<entry>
<title>为了那只小猫</title>
<url>/post/5da7c421/</url>
<content><![CDATA[<p>无私的关爱与付出,有时是为一个人,有时是为一件物。回望过去的12小时,我关爱与付出,却是为了那只小猫。</p>
<p>透过满是水垢的窗户,我看见了冬天的早晨。暗白色的浮云填满了天空,只留下几丝细小的裂缝,透过微弱的光。建筑物的窗台、楼顶,干燥的路面,铺满了稀稀拉拉的雪花。妈妈已经下楼开动汽车,我珍惜着出门上课之前在家的每一秒钟,渴望在这个寒冬中得到温存。</p>
<p>我无力地打开门,扫视楼道:栏杆拐角下蜷缩的是什么?黑煤球般的小猫咪!短短的毛,黑尾巴弯曲在身前,侧卧在地上。慢慢走近蹲下,逼近它来仔细端详,它竟丝毫不怕。再近一点……哇!周围是昏暗的光,它的两个小眼睛却发出微光,好像散布着求救信号。</p>
<p>手表的指针不断走动,强迫我接受上课的事实。我飞速冲到了车里,告诉妈妈这个猫咪的存在。一分钟后,小猫依偎在妈妈的胳膊里,出现在我眼前。考虑到它传染病的风险,只能先待在后备箱里。</p>
<p>15分钟的车程,一路风景。车窗上雪花稀少,每朵都显得十分清晰,六个花瓣向外延伸着,轻轻地伏在玻璃上,力量微弱,却十分牢固。迅捷的汽车猛一加速,风在车窗边一挥,几朵小雪花脱离了车窗,消失在茫茫的冬天中。而我坐在车里,十分温暖。我舒展地躺在车的后座,总是觉得身后有一块磁铁,强力地吸引着我,激发我内心的好奇与牵挂,并不断散发着光与热。我多想好好地端详一下它的圆脸蛋,撸一撸柔软的毛啊!</p>
<p>课上完回家,妈妈也带它去宠物医院,开药回家,所幸只是轻微感冒,但仍需隔离观察。黑煤球终于回到了我的身边:原来是如此瘦骨嶙峋的小猫!全身乌黑,脸上鼻子向前微探,两眼如同两只钨丝灯泡,圆形的角膜散发出黄色的亮光,狭窄的一条“钨丝”竖直跨越角膜,将我的灵魂完完全全吸附到它眼里。</p>
<p>之后的故事,讲不完了。</p>
<p>上课迟到,家与医院来回奔波,费尽心思的照顾……都是为了那只黑煤球,那只小猫。何苦呢?因为爱啊!</p>
]]></content>
<categories>
<category>随笔</category>
</categories>
<tags>
<tag>记叙文</tag>
</tags>
</entry>
<entry>
<title>从最简单的程序开始教你怎么破解(一)</title>
<url>/post/2615/</url>
<content><![CDATA[<p>28号我发了一篇关于破解迅雷的文章,如果你看到了,你就会先被那上千的字数吓住。但是破解(反编译)真的有那么难吗?作为一个不太“菜”的“菜鸟”,我想告诉你,其实破解一个软件只是找到思路后,改改二进制的逻辑罢了。但是真的又有这么简单吗?也不是。</p>
<p>如果你刚接触反编译、破解软件,那就从自己开发的最简单的小程序开始破解。今天这篇文章所讲的,是关于如何在MacOS Catalina系统里,使用Hopper Disassembler破解二进制文件。希望我可以通过这篇文章能帮到你。</p>
<h1 id="入手程序"><a href="#入手程序" class="headerlink" title="入手程序"></a>入手程序</h1><p>我们想要破解,可以先从最简单的程序开始——<code>Crackmes</code>。<code>Crackme</code>都是一些公开给别人尝试破解的小程序,制作<code>Crackme</code>的人可能是程序员,想测试一下自己的软件保护技术,也可能是一位<code>Cracker</code>,想挑战一下其它<code>Cracker</code>的破解实力,也可能是一些正在学习破解的人,自己编一些小程序给自己破。<code>CrackMe</code>简称<code>CM</code>。有不少论坛的<code>CM</code>大赛就是指这个。我用<code>Swift</code>语言做了几个简单的<code>Crackme</code>,来教你们怎么破解。先看看第一个CM:</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed/2020/1/29/hop-cm1-thumb.png" alt="CM #1"></p>
<p>我的这个程序首先会用中文打印出说明书和破解要求,然后要求你输入一个固定的序列号,才能激活,如果错误,会提示让你再输入一遍,直到输入正确为止。而你的工作,就是把程序改成输入任意序列号都能激活。</p>
<h1 id="分析程序"><a href="#分析程序" class="headerlink" title="分析程序"></a>分析程序</h1><p>将这个二进制拖入 Hopper (关于 Hopper 的使用,《破解迅雷》那篇文章有所涉及,不过有时间我可以专门出一篇文章讲 Hopper 的使用,这里不再阐述)分析。分析完成后, Hopper 应该会跳转到<code>_main</code>函数。类似下图:</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/1/29/hop-cm1-mainfunc.png" alt="main函数"></p>
<p>有人说,不对啊,<code>Swift</code>语言没有<code>main</code>主函数这一说啊,为什么会有它呢?因为,<code>Swift</code>中,写法里没有主函数,但是<code>Swift</code>代码是从上往下依次执行的,因袭编译器在编译中,自然会解析到一种进程,但它不能称作主函数,只能用<strong>主进程</strong>这样的词<strong>形容</strong>。因此,所有代码都会出现在这里。</p>
<p>面对这么多复杂的汇编指令怎么办?自然是要瞧一瞧伪代码:</p>
<pre class="line-numbers language-objectivec" data-language="objectivec"><code class="language-objectivec">int _main(int arg0, int arg1) {
rax = Swift.String.init(82mal9-29anyw-9msfz6, 0x14, 0x1);
rsi = *type metadata for Any;
*Crackme__1.serial : Swift.String = rax;
*qword_100002050 = 0x1;
var_20 = Swift._allocateUninitializedArray(0x1, rsi + 0x8);
rax = Swift.String.init(\xE8\xAF\xB4\xE6\x98\x8E\xEF\xBC\x9A\xE6\xAD\xA4\xE7\xA8\x8B\xE5\xBA\x8F\xE8\xA6\x81\xE6\xB1\x82\xE4\xBD\xA0\xE8\xBE\x93\xE5\x85\xA5\xE4\xB8\x80\xE4\xB8\xAA\xE5\x9B\xBA\xE5\xAE\x9A\xE7\x9A\x84\xE5\xBA\x8F\xE5\x88\x97\xE5\x8F\xB7\xEF\xBC\x8C\xE6\x89\x8D\xE8\x83\xBD\xE6\xBF\x80\xE6\xB4\xBB\xE3\x80\x82\n\xE8\xA6\x81\xE6\xB1\x82\xEF\xBC\x9A\xE6\x8A\x8A\xE7\xA8\x8B\xE5\xBA\x8F\xE6\x94\xB9\xE6\x88\x90\xE8\xBE\x93\xE5\x85\xA5\xE4\xBB\xBB\xE6\x84\x8F\xE5\xBA\x8F\xE5\x88\x97\xE5\x8F\xB7\xE9\x83\xBD\xE8\x83\xBD\xE6\xBF\x80\xE6\xB4\xBB\xE3\x80\x82\n, 0x89, 0x0, 0x0);
*(int64_t *)0x19 = *type metadata for Swift.String;
*(int64_t *)0x1 = rax;
*(int64_t *)0x9 = 0x0;
var_28 = default argument 1 of Swift.print();
Swift.print(var_20, var_28, 0x0, default argument 2 of Swift.print(), 0x0);
swift_bridgeObjectRelease(0x0);
swift_bridgeObjectRelease(0x0);
swift_bridgeObjectRelease(var_20);
Crackme__1.verify(var_20, var_28);
return 0x0;
}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>虽然我不太懂OC,也不太懂这代码说的是什么,但是至少知道了如下几点(看代码注释):</p>
<pre class="line-numbers language-objectivec" data-language="objectivec"><code class="language-objectivec">int _main(int arg0, int arg1) { // main函数
rax = Swift.String.init(82mal9-29anyw-9msfz6, 0x14, 0x1); // Swift的字符串初始化。从字符串看,很可能是注册码:82mal9-29anyw-9msfz6
...
rax = Swift.String.init(\xE8\xAF\xB4\xE6\x98\x8E\xEF\xBC\x9A\xE6\xAD\xA4\xE7\xA8\x8B\xE5\xBA\x8F\xE8\xA6\x81\xE6\xB1\x82\xE4\xBD\xA0\xE8\xBE\x93\xE5\x85\xA5\xE4\xB8\x80\xE4\xB8\xAA\xE5\x9B\xBA\xE5\xAE\x9A\xE7\x9A\x84\xE5\xBA\x8F\xE5\x88\x97\xE5\x8F\xB7\xEF\xBC\x8C\xE6\x89\x8D\xE8\x83\xBD\xE6\xBF\x80\xE6\xB4\xBB\xE3\x80\x82\n\xE8\xA6\x81\xE6\xB1\x82\xEF\xBC\x9A\xE6\x8A\x8A\xE7\xA8\x8B\xE5\xBA\x8F\xE6\x94\xB9\xE6\x88\x90\xE8\xBE\x93\xE5\x85\xA5\xE4\xBB\xBB\xE6\x84\x8F\xE5\xBA\x8F\xE5\x88\x97\xE5\x8F\xB7\xE9\x83\xBD\xE8\x83\xBD\xE6\xBF\x80\xE6\xB4\xBB\xE3\x80\x82\n, 0x89, 0x0, 0x0); // 这一大长串的东西也是字符串初始化,应该是中文,可能是“说明”和“要求”的字符串
...
var_28 = default argument 1 of Swift.print(); // 打印了那一长串中文
Swift.print(var_20, var_28, 0x0, default argument 2 of Swift.print(), 0x0); // 打印了那一长串中文
...
Crackme__1.verify(var_20, var_28); // 调用了一个叫verify的函数
return 0x0; // 程序结束
}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>最吸引人的是<code>verify()</code>函数,它有两个参数:<code>var_20</code>和<code>var_28</code>。这个函数把这两个参数做比较,最后打印出结果。因为函数后面没有打印结果的操作,所以这个打印结果的操作应该在函数里。</p>
<p>双击这个函数,就能查看。这个函数不短(其实源代码很短),我们来看关键点:</p>
<pre class="line-numbers language-objectivec" data-language="objectivec"><code class="language-objectivec">int _$s10Crackme__16verifyyyF(int arg0, int arg1) {
... // 定义了一些东西,还打印了说明和要求
if ((var_89 & 0x1) == 0x0) {
// 判断语句
}
else {
// 判断语句
}
rax = outlined consume of Swift.String?(var_70, 0x1);
return rax; // 执行一些结束用的代码
}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>我们只要让程序执行正确的语句就行了。但是怎么判断条件语句中<code>if</code>是正确的,还是<code>else</code>是正确的呢?经过仔细观察,我们看见了<code>if</code>中有一个代码:</p>
<pre class="line-numbers language-objectivec" data-language="objectivec"><code class="language-objectivec">Crackme__1.verify(var_D0, rsi);<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p>按照程序的思路,用户输错了,程序会提示你错误,然后再次调用函数,让用户重新输入序列号。函数中会嵌套一个同样的函数,不难看出这是<strong>递归</strong>,按照思路走,递归一般会使用在用户序列号错误的情况,因此:<code>if</code>中是用户输错的情况,<code>else</code>是输对的情况。只要让程序执行<code>else</code>代码块即可。</p>
<h1 id="修改程序"><a href="#修改程序" class="headerlink" title="修改程序"></a>修改程序</h1><p>随便在<code>else</code>中选一段代码,切回<code>CFG Mode</code>,可以看到,这段代码在<code>loc_100000ba2</code>里,因此<code>loc_100000ba2</code>是正确代码。沿着上面的箭头找,就会发现上面的主程序是<code>_$s10Crackme__16verifyyyF</code>函数,只需把函数下的<code>jne</code>做一个硬性跳转,换成<code>jmp</code>:</p>
<pre class="line-numbers language-nasm" data-language="nasm"><code class="language-nasm">jmp loc_100000ba2<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/1/29/hop-cm2-jmp.jpg" alt="换成jmp"></p>
<p>之后按回车。</p>
<h1 id="修改完成"><a href="#修改完成" class="headerlink" title="修改完成"></a>修改完成</h1><p>回车后,你的 Hopper 可能会出现这个诡异的画面:</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/1/29/hop-cm1-cfgbug.png" alt="CFG bug"></p>
<p>不用怕,这只是 Hopper 的 CFG bug。之所以它显示这个画面,是因为你在CFG模式下,然而你的光标自动跳转到了白色区域的代码,这段代码已经不存在了(被<code>ret</code>了)。因此随便点击 Hopper 的任意位置(白色区域代码外),就可以回到 CFG 了。接下来,让我们在纵观一下 CFG:</p>
<img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2020/1/29/hop-cm2-cfg.png" alt="纵观 CFG" style="zoom:50%;" />
<p>怎么样?刚才的两个分支变成了一个分支了。运行一下程序,无论输入什么,都可以成功激活。这么鼓捣下来,还是挺有趣的。</p>
<blockquote>
<p>再次声明:<strong>无论任何人使用文章中的技术破解任何软件造成的后果,请自行承担</strong>。 </p>
</blockquote>
]]></content>
<categories>
<category>计算机</category>
</categories>
<tags>
<tag>反编译</tag>
</tags>
</entry>
<entry>
<title>什么?不用中间变量也能交换两个变量的值?</title>
<url>/post/4976cae6/</url>
<content><![CDATA[<p>什么?不用中间变量也能交换两个变量的值?当然可以。本文就来深入讨论一下交换变量的那些事。</p>
<h1 id="使用中间变量"><a href="#使用中间变量" class="headerlink" title="使用中间变量"></a>使用中间变量</h1><p>假设有两个变量a和b,如何交换变量的值?<code>a = b; b = a</code>?不可以。因为计算机的特点是一个周期只能执行一个指令,不可能有两个操作同时发生。所以我们要借助一个中间变量,使两个变量在交换的时候,一个变量有地方等待另一个变量交换完毕,再进行交换。为了行文方便,变量的类型为整数。如下图:</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2021/07/19/swap_var.gif" alt="使用中间变量(本图原创,严禁盗用)"></p>
<pre class="line-numbers language-c" data-language="c"><code class="language-c">void swap_var(int *a, int *b) {
int c = *a;
*a = *b;
*b = c;
}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>这样的代码简洁易懂,是编程的最佳选择。作为一名爱好者,让我们继续探索……</p>
<h1 id="加减法"><a href="#加减法" class="headerlink" title="加减法"></a>加减法</h1><p>能不能使用简单的加减运算交换数值呢?将a、b两数相加,得到的和减去a得到b,减去b得到a。带着这个理论实践一下:</p>
<pre class="line-numbers language-c" data-language="c"><code class="language-c">void swap_add(int *a, int *b) {
int c = *a + *b;
*a = c - *a; // *a == *a + *b - *a == *b
*b = c - *a; // *b == *a + *b - *b = *a
}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>在上述代码中,给<code>*a</code>和<code>*b</code>赋值的都是<code>c - *a</code>,这是因为两次<code>*a</code>的值不同。第一次<code>*a == *a</code>,第二次a的值变为b,即<code>*a == c - *a == *b</code>。</p>
<p>我们现在想把第三方变量<code>c</code>省去,解决办法是将a与b的和加到a一个人身上。</p>
<pre class="line-numbers language-c" data-language="c"><code class="language-c">void swap_add(int *a, int *b) {
*a += *b; // a的值等同于上面的c
*b = *a - *b; // 这里的a是a与b的和,减去b自然等于a,将a赋值给b
*a -= *b; // 这里的a仍然是a与b的和,减去b自然等于a
}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>减法与加法原理相同,符号不同,故不再阐述。</p>
<h1 id="乘除法"><a href="#乘除法" class="headerlink" title="乘除法"></a>乘除法</h1><p>加减法可以实现,乘除法必定也可以实现数值交换。将加减法的原理稍加改变可得:将a、b两数相乘,得到的积除以a得到b,除以b得到a。带着这个理论实践一下:</p>
<pre class="line-numbers language-c" data-language="c"><code class="language-c">void swap_mul(int *a, int *b) {
int c = *a * *b;
*a = c / *a; // a == a * b / a == b
*b = c / *a; // b == a * b / b = a
}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>我们将b乘到a上,得:</p>
<pre class="line-numbers language-c" data-language="c"><code class="language-c">void swap_mul(int *a, int *b) {
*a *= *b;
*b = *a / *b; // 积÷b=a
*a /= *b; // 积÷a=b(这里b值已经变为a)
}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>除与乘大同小异,不再阐述。</p>
<h1 id="逻辑运算"><a href="#逻辑运算" class="headerlink" title="逻辑运算"></a>逻辑运算</h1><p>让我们进一步提问:逻辑运算可否实现功能呢?用加减乘除交换数值的原理,是算得与两数都有关的一个结果,已知一数可以求另一个数。那么逻辑运算是否可行?哪种逻辑运算有这一特性?</p>
<p>为了方便理解,应当使用二进制。我们来观察一下二元逻辑运算的真值表:</p>
<table>
<thead>
<tr>
<th align="center">a</th>
<th align="center">b</th>
<th align="center">与</th>
<th align="center">或</th>
<th align="center">异或</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">1</td>
</tr>
</tbody></table>
<p>我们首先排除与运算。因为当结果为0时,无法通过一个参数推断出另一个参数的真假性。</p>
<p>再排除或运算。当结果为1时,只要有一个参数为1即满足结果,与另一参数无关。</p>
<p>然而异或值得探讨。异或规定,相同为1,不同为0。我们只有两个参数,所以如果知道一个参数,还知道另一个参数与这个参数是否相同,我们即可求出另一参数。代码如下:</p>
<pre class="line-numbers language-c" data-language="c"><code class="language-c">void swap_xor(int *a, int *b) {
*a ^= *b; // a存储的是a与b每一位是否相同
*b ^= *a; // b依据相同性还原出a
*a ^= *b; // a依据相同性还原出b
// 上述代码可简写:*a ^= *b ^= *a ^= *b;
}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h1 id="进一步探讨"><a href="#进一步探讨" class="headerlink" title="进一步探讨"></a>进一步探讨</h1><p>为什么上述方法能实现两个值的交换?它们有什么共同点?</p>
<p>从代码上来看,每一个方法都是使用了不同的量互相推导,因为一步不能做两件事情是必然的;从逻辑上来看,待交换的数值不管怎样需要一个贮存点,可以是一个第三方变量,也可以与其中一个变量一起储存,前提是一起储存的两个变量可以被第三个变量分离。</p>
<p>我觉得我们的认知已经达到一个新的高度上了。</p>
<h1 id="本文代码"><a href="#本文代码" class="headerlink" title="本文代码"></a>本文代码</h1><pre class="line-numbers language-c" data-language="c"><code class="language-c">#include <stdio.h>
#include <time.h>
void swap_add(int *a, int *b) {
*a += *b;
*b = *a - *b;
*a -= *b;
}
void swap_mul(int *a, int *b) {
*a *= *b;
*b = *a / *b;
*a /= *b;
}
void swap_xor(int *a, int *b) {
// *a ^= *b;
// *b ^= *a;
// *a ^= *b;
*a ^= *b ^= *a ^= *b;
}
void swap_var(int *a, int *b) {
int c = *a;
*a = *b;
*b = c;
}
int main() {
int a = 0b101101;
int b = 0b1010010101;
time_t start, end;
printf("------xor-------\n");
printf("a1: %d\n", a);
printf("b1: %d\n", b);
start = time(NULL);
swap_xor(&a, &b);
end = time(NULL);
printf("a2: %d\n", a);
printf("b2: %d\n", b);
printf("time=%d\n",difftime(end,start));
printf("------add-------\n");
printf("a1: %d\n", a);
printf("b1: %d\n", b);
start = time(NULL);
swap_add(&a, &b);
end = time(NULL);
printf("a2: %d\n", a);
printf("b2: %d\n", b);
printf("time=%d\n",difftime(end,start));
printf("------mul-------\n");
printf("a1: %d\n", a);
printf("b1: %d\n", b);
start = time(NULL);
swap_mul(&a, &b);
end = time(NULL);
printf("a2: %d\n", a);
printf("b2: %d\n", b);
printf("time=%d\n",difftime(end,start));
printf("------nor-------\n");
printf("a1: %d\n", a);
printf("b1: %d\n", b);
start = time(NULL);
swap_var(&a, &b);
end = time(NULL);
printf("a2: %d\n", a);
printf("b2: %d\n", b);
printf("time=%d\n",difftime(end,start));
return 0;
}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
]]></content>
<categories>
<category>计算机</category>
</categories>
<tags>
<tag>C</tag>
</tags>
</entry>
<entry>
<title>从最简单的程序开始教你怎么破解(二)</title>
<url>/post/39501/</url>
<content><![CDATA[<p>今天,我们还讲Hopper反编译,不过用的还是昨天的<code>CM</code>。我今天准备告诉大家,怎样替换程序里的字符串,这样就可以为所欲为了。</p>
<blockquote>
<p>目标:把程序修改成,只有使用自己规定的注册码,才能激活成功。 </p>
</blockquote>
<h1 id="开始分析"><a href="#开始分析" class="headerlink" title="开始分析"></a>开始分析</h1><p>首先,程序拖入 Hopper 解析。完成后,在左边的搜索视图中选择<code>Str</code>,查看程序里的字符串。因为程序比较小,所以字符串只有5个。</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed/2020/1/29/hop-cm2-search-str.png" alt="查看字符串"></p>
<p>这五个字符串,前面一个是激活码(怎么判断出来的上篇文章讲过),中间四个其实是中文的十六进制编码(对应的是“说明”“要求”和“输入错误”几个字符串),最后一个是定义用来处理换行的。既然要修改第一个,我们就点进去。</p>
<h1 id="修改程序"><a href="#修改程序" class="headerlink" title="修改程序"></a>修改程序</h1><p>修改字符串有两种方法。</p>
<h2 id="方法一:修改十六进制"><a href="#方法一:修改十六进制" class="headerlink" title="方法一:修改十六进制"></a>方法一:修改十六进制</h2><ol>
<li><p>把视图切换为<code>Hex Mode</code>,这个模式已经自动给你高亮了对应激活码的16进制码。</p>
<p><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed/2020/1/29/hop-cm2-16bit.png" alt="16进制码"></p>
</li>
<li><p>例如,你要把激活码改成<code>111111-111111-111111</code>,那么,十六进制里,<code>1</code>对应<code>31</code>,而连字符<code>-</code>对应着<code>2D</code>,所以你就要把对应的十六进制改成:</p>
</li>
</ol>
<pre class="line-numbers language-none"><code class="language-none">31 31 31 31 31 31 2D 31 31 31 31 31 31 2D 31 31 31 31 31 31<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p>这里我要说明一点,如果你的激活码比以前的激活码要短,那么你就尽管从头开始修改,最后剩下几位,你就在相应的位置上填写<code>00</code>即可。如果激活码比以前的长,那么会出现一个修改文件偏移量的问题,比较复杂,再次不阐述。</p>
<p>例如,你的激活码是:<code>helloworld!</code>,对应下来就是:<code>68 65 6c 6c 6f 77 6f 72 6c 64 21</code><br>2. 如果你嫌麻烦,可以在右边对应的字符串一个个修改成<code>1</code>和<code>-</code>, Hopper 会自动为你生成十六进制填在左边。<br>3. <kbd>command</kbd>+<kbd>shift</kbd>+<kbd>E</kbd>,导出,完工!</p>
<h2 id="方法二:修改汇编代码"><a href="#方法二:修改汇编代码" class="headerlink" title="方法二:修改汇编代码"></a>方法二:修改汇编代码</h2><p>这个方法要比修改十六进制简单得多,我把它放在后面,是因为修改出来, Hopper 显示得有些让人迷糊,看不懂。我开始也用的这个方法,感觉错了,后来才知道这是最简单的方法。如果你用的是修改十六进制的方法,不妨撤回,然后用这个方法试试。</p>
<ol>
<li><p> 把视图切换回汇编代码,确保在激活码的地方。汇编代码应该长这样:</p>
<pre class="line-numbers language-nasm" data-language="nasm"><code class="language-nasm"> a82mal929anyw9m:
0000000100000e80 db "82mal9-29anyw-9msfz6", 0 ; DATA XREF=_main+8
0000000100000e95 db "", 0
0000000100000e96 db "", 0
0000000100000e97 db "", 0
0000000100000e98 db "", 0 ; DATA XREF=_$s10Crackme__16verifyyyF+118, _$s10Crackme__16verifyyyF+653
0000000100000e99 db "", 0
0000000100000e9a db "", 0
0000000100000e9b db "", 0
0000000100000e9c db "", 0
0000000100000e9d db "", 0
0000000100000e9e db "", 0
0000000100000e9f db "", 0<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li>
<li><p>鼠标选中第一行。我的第一行是指:<br> <code>0000000100000e80 db &quot;82mal9-29anyw-9msfz6&quot;, 0 ; DATA XREF=_main+8</code><br> 如果你不是这一行,会发生不好的结果。</p>
</li>
<li><p> <kbd>option</kbd>+<kbd>A</kbd>修改,输入指令:<code>db “你想要的激活码”, 0</code></p>
</li>
<li><p> 之后,你的那行代码就会改成这样:</p>
<pre class="line-numbers language-nasm" data-language="nasm"><code class="language-nasm">0000000100000e80 db 6 dup (0x31), 0x2d, 0x31 ; DATA XREF=_main+8
0000000100000e88 db 5 dup (0x31), 0x2d, 2 dup (0x31)
0000000100000e90 db 4 dup (0x31), 0x00<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>
<p>很奇怪吧!这就是一个坑:这段代码看起来怪怪的。为什么呢?<br>我提醒一下大家:</p>
</li>
<li><p> <code>db</code>命令是定义字节,基本上常量和变量的定义都会出现,不必纠结。</p>
</li>
<li><p> <code>dup</code>是复制的意思,对应英文单词<code>duplicate</code>。那么<code>6 dup (...)</code>是什么意思,就可想而知了。</p>
</li>
<li><p> <code>0x31</code>和<code>0x2d</code>都是刚才说过的十六进制。<br>现在你就会明白,这段代码是没有问题的。</p>
</li>
</ol>
<h1 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h1><p>提醒大家注意几点:</p>
<ul>
<li> 十六进制码<strong>不区分大小写</strong>;</li>
<li> 汉字也可以转化为十六进制。如果你在程序中看到类似<code>x98\xH7\x2B\x..\x..</code>这样的东西,是中文的十六进制编码,不是乱码。你可以到<a href="http://tools.jb51.net/tools/base64_decode-gb2312.php">相关解码网站</a>解码成中文。</li>
<li> 如果你需要十六进制的加解密,可以试试<a href="http://www.bejson.com/convert/ox2str/">BeJson</a>等工具。</li>
<li> 在HDA里输入命令时,用不着纠结汇编指令与参数之间的空格,空一个格就好, Hopper 会自动给你补充的。</li>
</ul>
]]></content>
<categories>
<category>计算机</category>
</categories>
<tags>
<tag>反编译</tag>
</tags>
</entry>
<entry>
<title>从某加速器的逆向中学习调试分析</title>
<url>/post/7ceda6f2/</url>
<content><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>我电脑上的<code>brew</code>好久没有更新维护了,GitHub也许久没有登录,原因仅有一个,就是登不上去。在这个时代免费、合法的加速器越来越少。好不容易找到了一个加速器,对外声称免费,但打开一看早已因为缺乏资金改成了付费。<br><img src="https://cdn.jsdelivr.net/gh/TLHorse/TLBlogBed@master/2022/01/30/pc-1.png" alt="软件界面"><br>它就是我们今天破解的目标。</p>
<h1 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h1><p>我们可以事先把二进制用Hopper加载,观察软件不难发现,使用C语言写的,UI框架是Qt,而且大部分的方法和符号已经被混淆。这为我们的逆向增加了难度,因此我们把字符串作为逆向的突破口,进行破解。</p>
<p>再次启动软件,观察可能有用的字符串:该软件在启动的时候会显示提示框“套参(原软件参字打错了)已使用完毕,请充值<del>”,点击“点我加速”按钮时,又会出现提示框“套参已使用完毕,请充值</del>”。请记住下面的八个字,它们是我们逆向的关键。</p>
<blockquote><span class="custom-blockquote-svg"><svg width="24" height="24" viewBox="0 0 24 24" fill="" xmlns="http://www.w3.org/2000/svg" data-reactroot="">
<path fill="" d="M22 12C22 6.5 17.5 2 12 2C6.5 2 2 6.5 2 12C2 17.5 6.5 22 12 22C13.8 22 15.5 21.5 17 20.6L22 22L20.7 17C21.5 15.5 22 13.8 22 12Z" undefined="1"></path>
<path fill="" d="M15.97 11.5H16.04C17.12 11.5 18 12.38 18 13.47V13.53C18 14.62 17.12 15.5 16.03 15.5H15.96C14.88 15.5 14 14.62 14 13.53V13.46C14 12.38 14.88 11.5 15.97 11.5Z" undefined="1"></path>
<path fill="" d="M7.97 11.5H8.04C9.12 11.5 10 12.38 10 13.47V13.53C10 14.62 9.12 15.5 8.03 15.5H7.97C6.88 15.5 6 14.62 6 13.53V13.46C6 12.38 6.88 11.5 7.97 11.5Z" undefined="1"></path>
<path stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" stroke="" d="M17 8.5C15.23 8.97 14.07 10.84 14.01 13.27C14 13.33 14 13.4 14 13.47V13.5"></path>
<path stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" stroke="" d="M9 8.5C7.23 8.97 6.07 10.84 6.01 13.27C6 13.33 6 13.4 6 13.47V13.5"></path>
<path stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" stroke="" d="M15.97 11.5H16.04C17.12 11.5 18 12.38 18 13.47V13.53C18 14.62 17.12 15.5 16.03 15.5H15.96C14.88 15.5 14 14.62 14 13.53V13.46C14 12.38 14.88 11.5 15.97 11.5Z"></path>
<path stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" stroke="" d="M7.97 11.5H8.04C9.12 11.5 10 12.38 10 13.47V13.53C10 14.62 9.12 15.5 8.03 15.5H7.97C6.88 15.5 6 14.62 6 13.53V13.46C6 12.38 6.88 11.5 7.97 11.5Z"></path>
</svg>
</span><p>套参已使用完毕,请充值~</p></blockquote>
<p>除了两个弹窗之外,该软件还需要注册。据我观察,在未注册情况下注册弹窗会代替套餐弹窗。(因为一开始下载软件后我直接注册了,所以破解中没发现这一点。)我们希望在不注册的情况下使用。</p>
<p>综上所述我们的破解目标有4点:</p>
<ol>
<li>软件启动时的套餐弹窗;</li>
<li>软件启动时的注册弹窗;</li>
<li>点我加速时的套餐弹窗;</li>
<li>点我加速时的注册弹窗。</li>
</ol>