0%

Python逆向

记录一下比赛中遇到的Python逆向学习心得

CPython逆向

如果python程序中调用了一个打包成.pyd或者.so的外部连接库 那么那个库大概率就是Cython编译打包的 直接当成.dll或者.so来逆向

正式开始逆向之前需要先知道几个关于CPython的最基础的知识点 方便后续理解

PyObject

python中所有类都基于一个最基础的Object类 而Cython做的事无非就是将python实现转为C实现 原本的Object类在CPython中这个类就是PyObject结构体 逆向过程中见到的所有python变量都是这个类的派生类 这个类的内存布局如下:

1
2
3
4
5
6
7
struct Py_obj
{
_QWORD refcnt; // 引用次数 用于内存回收机制
Py_obj *type; // 变量的类型
_QWORD size; // 变量的大小
_QWORD item; // 变量的值
};

尽管一些特殊的类(如 PyUnicode, PyTuple, …)在值的存储方式上与最基础的类有些许不同之外 所有类型的python原生类都遵循这个结构体布局 其中需要特别说明的是PyLong

PyLong

它的内存分布依然遵循上述规则 如:

image-20241223095615394

但是存放的值不能简单的直接通过小端序转化成真正表示的数值 python为了防止溢出 每个_DWORD只使用其中的30bits用于存放数据(30/32) 而PyLong中的size字段表示的是所存放的值占 (size // 8) * 30 bits的内存 因此在不查找全局构造表单纯靠动调来获取某个CPython整型的值时需要做一些特殊的处理 这里给出一个获取CPython整型时可以用的脚本:

1
2
3
4
5
6
digtsLen = (size // 8)
digtsAddr = PyobjAddr + 8 * 3
pynum = 0
for i in range(digtsLen):
t = int.from_bytes(get_bytes(digtsAddr + i * 4, 4), 'little') & 0x3FFFFFFF
pynum |= t << (30 * i)

调试方法

直接当动态链接库调试 要附加到的程序就是对应题目用来编译的python版本的python解释器

开始分析

寻找初始化函数

正式开始逆向的第一步就是找到python中使用到的所有字面值 例如:

1
2
for i in range(4):
print(i * 0x10)

其中就有4, 0x10两个字面值 在Cython打包python代码时类似这两个的所有字面值都会在一个函数(__Pyx_InitConstants 但是通常编译出来的动态链接库都会去符号表所以知道符号没什么用)中进行初始化:

1
2
3
table = [0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x100]
for x in table:
print(table)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static CYTHON_SMALL_CODE int __Pyx_InitConstants(void) {
if (__Pyx_CreateStringTabAndInitStrings() < 0) __PYX_ERR(0, 1, __pyx_L1_error);
__pyx_int_1 = PyInt_FromLong(1); if (unlikely(!__pyx_int_1)) __PYX_ERR(0, 1, __pyx_L1_error)
__pyx_int_2 = PyInt_FromLong(2); if (unlikely(!__pyx_int_2)) __PYX_ERR(0, 1, __pyx_L1_error)
__pyx_int_4 = PyInt_FromLong(4); if (unlikely(!__pyx_int_4)) __PYX_ERR(0, 1, __pyx_L1_error)
__pyx_int_8 = PyInt_FromLong(8); if (unlikely(!__pyx_int_8)) __PYX_ERR(0, 1, __pyx_L1_error)
__pyx_int_16 = PyInt_FromLong(16); if (unlikely(!__pyx_int_16)) __PYX_ERR(0, 1, __pyx_L1_error)
__pyx_int_32 = PyInt_FromLong(32); if (unlikely(!__pyx_int_32)) __PYX_ERR(0, 1, __pyx_L1_error)
__pyx_int_64 = PyInt_FromLong(64); if (unlikely(!__pyx_int_64)) __PYX_ERR(0, 1, __pyx_L1_error)
__pyx_int_128 = PyInt_FromLong(128); if (unlikely(!__pyx_int_128)) __PYX_ERR(0, 1, __pyx_L1_error)
__pyx_int_256 = PyInt_FromLong(256); if (unlikely(!__pyx_int_256)) __PYX_ERR(0, 1, __pyx_L1_error)
return 0;
__pyx_L1_error:;
return -1;
}

可以看出特征就是大量调用PyInt_FromLong 在IDA中是这个宏对应的函数PyLong_FromLong 通过交叉引用这个函数找到大量调用了它的函数 这个函数大概率就是__Pyx_InitConstants 除此之外一些长度过长的整型也会通过从字符串转化成PyLong的方式初始化 这时候只需要在Strings中找到纯数字字符串就能定位到初始化函数:

image-20241223131635173

image-20241223131653876

寻找目标函数

在python中实现的函数在Cython编译后在函数的结尾会有__Pyx_AddTraceback("Module_Name.Function_Name", __pyx_clineno, __pyx_lineno, __pyx_filename); 也就是说只要找到Module_Name.Function_Name这个字符串就能找到目标函数

将之前的代码写到函数里:

1
2
3
4
5
6
# test.pyx

def test():
table = [0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x100]
for x in table:
print(table)

再用Cython编译一次 找到模块名和函数名(这里模块名就是xxx.pyd中的xxx):

image-20241223133248174

交叉引用就能找到对应的函数:

image-20241223133419317

有时候出现了交叉引用的结果不止一个 是因为在别的地方调用到了这个函数 在Cython中调用函数需要写进一个wrapper里 其中也会包含了__Pyx_AddTraceback("Module_Name.Function_Name", __pyx_clineno, __pyx_lineno, __pyx_filename); 定位到目标函数也很简单 因为这里作为wrapper最后也是要调用目标函数的:

image-20241223133841700

还原python代码

首先就是在IDA中定义上面说的PyObject结构体方便逆向 然后在静态分析时需要需要忽略很多用于内存回收的代码块 特征就是调用了Py_Dealloc函数 参数就是要释放的变量 而这些都是系统的事 在还原代码上几乎没有帮助 所以这部分代码块都可以忽略 如:

image-20241223150524555

此外还有产生变量时对变量的检查也可以忽略 基本上就是在调用了CPython的API后产生的变量会立马进行一次检测 这部分在预期内是一定会成功的 所以可以忽略

一般的python操作全部都会用CPython的API来实现 只要做好注释和变量命名很容易看出逻辑 这里只给出一些特殊结构

循环

以这两种循环为例:

1
2
3
4
5
6
7
def test():
table = [0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x100]
for x in table:
print(x)
lenth = len(table)
for i in range(lenth):
print(table[i])

在看CPython的实现时就能发现其实内部实现几乎就是一致的:

image-20241223175908405

image-20241223180101028

可以看到唯一的区别就是直接使用下标来访问时会对要访问的下标进行越界判断 若越界则会使用PyObject_GetItem来抛出错误 否则直接从底层访问列表tableitem成员 这个成员包含了9个PyLong对象([0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x100])

调用

另外一个重点就是分辨使用到的python内置函数 这些函数因为不属于CPython API的内容 都不会像上面的被识别出来的外部函数一样直接看得到函数名 而是像调用所有用户函数一样通过wrapper调用:

image-20241223181108180

在这里v5就是获取到的函数 v2就是对应的python函数对象:

image-20241223181316165

列表推导式

1
2
...
newtable = [x << 1 for x in table]

以此为例

得到伪代码:

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
while ( 1 ){
x_ = *(Py_obj **)(arr_->item + 8 * idx_);
if ( LODWORD(x_->refcnt) != -1 )
++LODWORD(x_->refcnt);
v32 = v31;
++idx_;
v31 = x_;
if ( v32 )
{
if ( SLODWORD(v32->refcnt) >= 0 )
{
v22 = v32->refcnt-- == 1LL;
if ( v22 )
Py_Dealloc(v32);
}
}
if ( x_->type != (Py_obj *)PyLong_Type[0] )
goto LABEL_90;
size = x_->size;
if ( (size & 1) == 0 )
{
if ( size >= 0x10 )
{
switch ( (x_->size >> 3) * (1 - (x_->size & 3uLL)) )
{
case 0xFFFFFFFFFFFFFFFEuLL:
v35 = -(__int64)(LODWORD(x_->item) | ((unsigned __int64)HIDWORD(x_->item) << 30));
goto LABEL_87;
case 2uLL:
v35 = LODWORD(x_->item) | ((unsigned __int64)HIDWORD(x_->item) << 30);
goto LABEL_87;
default:
v36 = (Py_obj *)(*(__int64 (__fastcall **)(Py_obj *, Py_obj *))(PyLong_Type[12] + 88LL))(x_, table[24]);
break;
}
}
else
{
v34 = LODWORD(x_->item) * (1 - (size & 3));
if ( v34 == (2 * v34) >> 1 || !v34 )
{
v36 = (Py_obj *)PyLong_FromLong((unsigned int)(2 * v34));
}
else
{
v35 = v34;
LABEL_87:
if ( v35 == (__int64)(2 * v35) >> 1 )
v36 = (Py_obj *)PyLong_FromLongLong(2 * v35, v35, PyLong_Type[0], table[24]);
else
LABEL_90:
v36 = (Py_obj *)PyNumber_Lshift(x_, table[24]);
}
}
x_ = v36;
goto LABEL_92;
}
if ( LODWORD(x_->refcnt) != -1 )
++LODWORD(x_->refcnt);
LABEL_92:
if ( !x_ )
break;
v37 = new_table->size;
if ( new_table[1].refcnt <= v37 )
{
if ( (unsigned int)PyList_Append(new_table, x_) )
{
v6 = 2529;
goto LABEL_105;
}
}
else
{
if ( LODWORD(x_->refcnt) != -1 )
++LODWORD(x_->refcnt);
*(_QWORD *)(new_table->item + 8 * v37) = x_;
new_table->size = v37 + 1;
}
if ( SLODWORD(x_->refcnt) >= 0 )
{
v22 = x_->refcnt-- == 1LL;
if ( v22 )
Py_Dealloc(x_);
}
if ( idx_ >= arr_->size )
goto LABEL_123;
}

可以看到实际上就是拆成了:

1
2
for x in table:
newtable.append(x)

其中大部分代码都在对取到的x的长度做对应处理 实际上看到这种结构都要知道进行的都是同一种运算 只用选择一种比较好理解的来判断是什么运算就行

实战

[Ciscn & C!C!B!2024] rand0m

1
2
3
4
5
6
import rand0m
help(rand0m)
flag = input("Please input: ")
if(rand0m.check(flag)):
print("Congrats! Flag is: flag{"+flag+"}")
else: print("Wrong! Try again!")

调用了rand0m.pyd中的check函数 定位完常量构造函数后查找rand0m.check字符串 交叉引用发现有两处用到:

image-20241223182920772

其中一处有报错信息 很明显就是check函数的wrapper:

image-20241223183029637

跳到第二处就是check函数的位置 实际分析没什么新东西 需要注意的只有忽略掉一些非用户的处理 直接给出分析结果:

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
Py_obj *__fastcall rand0m_check(Py_obj *a1, Py_obj *usr_input)
{
Py_obj *v2; // r12
Py_obj *v3; // rbx
Py_obj *v4; // rsi
Py_obj *v5; // r14
Py_obj *arr1; // r8
unsigned int v7; // ebp
unsigned int v8; // r13d
Py_obj **v9; // rdx
Py_obj *v10; // rcx
Py_obj *v11; // rcx
Py_obj *v12; // rcx
Py_obj *v13; // rcx
Py_obj *v14; // rcx
Py_obj *v15; // rcx
Py_obj *v16; // rcx
Py_obj *v17; // rcx
Py_obj *v18; // rcx
Py_obj *v19; // r13
int c4; // eax
Py_obj *v21; // rcx
Py_obj *i; // r15
bool v23; // zf
Py_obj **v24; // rdx
Py_obj *type; // rax
unsigned __int64 size; // rcx
Py_obj *i8; // rbp
Py_obj *k; // r14
Py_obj *v29; // rax
Py_obj *j_; // rax
Py_obj *j_slice; // rbx
Py_obj *v32; // rax
unsigned __int64 v33; // rcx
Py_obj *j8; // rsi
Py_obj *midd; // rdi
Py_obj *v36; // rax
Py_obj *usr_in; // r8
__int64 v38; // rbx
Py_obj *slicer; // rax
Py_obj *v40; // r12
Py_obj *v41; // rcx
Py_obj *string_rand0m; // rdi
Py_obj *Item_KnownHash; // rax
Py_obj *v44; // rcx
int v45; // eax
_DWORD *item; // rsi
int v47; // edx
Py_obj *res; // rbx
Py_obj *v49; // rcx
Py_obj *v50; // r12
Py_obj *v51; // rcx
Py_obj *v52; // rax
unsigned __int64 v53; // rcx
Py_obj *i2; // rsi
Py_obj *v55; // rax
int IsTrue; // ebx
Py_obj *v57; // rcx
Py_obj **v58; // rdx
Py_obj *v59; // rax
unsigned __int64 v60; // rcx
Py_obj *v61; // rsi
Py_obj *v62; // rax
int IsTrue_; // esi
Py_obj *v64; // rax
Py_obj *v65; // rcx
Py_obj *v66; // rdx
Py_obj *v67; // rbx
Py_obj *v68; // rax
Py_obj *v69; // rcx
Py_obj *key; // [rsp+30h] [rbp-88h]
Py_obj *v72; // [rsp+38h] [rbp-80h]
_QWORD v73[2]; // [rsp+50h] [rbp-68h] BYREF
int tmp1; // [rsp+C0h] [rbp+8h]
Py_obj *v76; // [rsp+D0h] [rbp+18h]
Py_obj *v77; // [rsp+D8h] [rbp+20h]

v2 = usr_input;
v3 = 0LL;
v4 = 0LL;
v5 = 0LL;
key = 0LL;
v76 = 0LL;
v72 = 0LL;
arr1 = PyList_New(8LL); // arr1 = [None] * 8
if ( !arr1 )
{
v7 = 13;
v8 = 2861;
goto LABEL_225;
}
v9 = table;
v10 = table[40];
if ( LODWORD(v10->refcnt) != -1 )
++LODWORD(v10->refcnt);
*(_QWORD *)arr1->item = v9[40];
v11 = v9[43];
if ( LODWORD(v11->refcnt) != -1 )
++LODWORD(v11->refcnt);
*(_QWORD *)(arr1->item + 8LL) = v9[43];
v12 = v9[41];
if ( LODWORD(v12->refcnt) != -1 )
++LODWORD(v12->refcnt);
*(_QWORD *)(arr1->item + 16LL) = v9[41];
v13 = v9[47];
if ( LODWORD(v13->refcnt) != -1 )
++LODWORD(v13->refcnt);
*(_QWORD *)(arr1->item + 24LL) = v9[47];
v14 = v9[39];
if ( LODWORD(v14->refcnt) != -1 )
++LODWORD(v14->refcnt);
*(_QWORD *)(arr1->item + 32LL) = v9[39];
v15 = v9[45];
if ( LODWORD(v15->refcnt) != -1 )
++LODWORD(v15->refcnt);
*(_QWORD *)(arr1->item + 40LL) = v9[45];
v16 = v9[42];
if ( LODWORD(v16->refcnt) != -1 )
++LODWORD(v16->refcnt);
*(_QWORD *)(arr1->item + 48LL) = v9[42];
v17 = v9[46];
if ( LODWORD(v17->refcnt) != -1 )
++LODWORD(v17->refcnt);
key = arr1;
*(_QWORD *)(arr1->item + 56LL) = v9[46];
v18 = v9[29];
if ( LODWORD(v18->refcnt) != -1 )
++LODWORD(v18->refcnt);
v19 = v9[29]; // arr1 = [0x112287f38, 0x218d24b3a, 0x10a30f74d, 0x320f1db77, 0x1023a1268, 0x22df38403, 0x208108807, 0x318499bb6]
c4 = 0;
v77 = v19;
tmp1 = 0;
while ( 1 )
{
i = PyLong_FromLong((unsigned int)c4); // for i in range(4):
if ( !i )
{
v7 = 15;
v8 = 2908;
goto LABEL_223;
}
if ( v4 )
{
if ( SLODWORD(v4->refcnt) >= 0 )
{
v23 = v4->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(v4);
}
}
v24 = table;
type = (Py_obj *)i->type;
if ( type == PyLong_Type )
{
size = i->size;
if ( (size & 1) != 0 )
{
if ( LODWORD(i->refcnt) != -1 )
++LODWORD(i->refcnt);
i8 = i;
k = i;
goto LABEL_40;
}
v29 = size >= 0x10
? (Py_obj *)(*(__int64 (__fastcall **)(Py_obj *, Py_obj *))(*((_QWORD *)&PyLong_Type + 12) + 16LL))(
i,
table[34])
: PyLong_FromLongLong((Py_obj *)(8LL * (int)(LODWORD(i->item) * (1 - (size & 3)))));
}
else
{
v29 = type == (Py_obj *)PyFloat_Type ? PyFloat_FromDouble(v21) : PyNumber_Multiply(i, table[34]);
}
i8 = v29; // i8 = i * 8
k = v29;
if ( !v29 )
{
v7 = 16;
v8 = 2920;
v4 = i;
goto LABEL_223;
}
v24 = table;
LABEL_40:
j_ = Add(i, v24[30]);
j_slice = j_; // j = i + 1
if ( !j_ )
{
v8 = 2922;
midd = 0LL;
goto LABEL_208;
}
v32 = (Py_obj *)j_->type;
if ( v32 == PyLong_Type )
{
v33 = j_slice->size;
if ( (v33 & 1) != 0 )
{
if ( LODWORD(j_slice->refcnt) != -1 )
++LODWORD(j_slice->refcnt);
j8 = j_slice;
midd = j_slice;
goto LABEL_53;
}
v36 = v33 >= 0x10
? (Py_obj *)(*(__int64 (__fastcall **)(Py_obj *, Py_obj *))(*((_QWORD *)&PyLong_Type + 12) + 16LL))(
j_slice,
table[34])
: PyLong_FromLongLong((Py_obj *)(8LL * (int)(LODWORD(j_slice->item) * (1 - (v33 & 3)))));
}
else
{
v36 = v32 == (Py_obj *)PyFloat_Type ? PyFloat_FromDouble((Py_obj *)table) : PyNumber_Multiply(j_slice, table[34]);
}
j8 = v36; // j8 = j * 8
midd = v36;
if ( !v36 )
{
v8 = 2924;
goto LABEL_208;
}
LABEL_53:
if ( SLODWORD(j_slice->refcnt) >= 0 )
{
v23 = j_slice->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(j_slice);
}
usr_in = (Py_obj *)v2->type;
v38 = usr_in[3].size;
if ( !v38 || !*(_QWORD *)(v38 + 8) )
{
PyErr_Format(PyExc_TypeError, "'%.200s' object is unsliceable", (const char *)usr_in->item);
LABEL_204:
j_slice = 0LL;
LABEL_205:
v8 = 2927;
LABEL_208:
v7 = 16;
goto LABEL_209;
}
slicer = PySlice_New(i8, j8, Py_NoneStruct);// 切片
v40 = slicer;
if ( !slicer )
goto LABEL_204;
j_slice = (Py_obj *)(*(__int64 (__fastcall **)(Py_obj *, Py_obj *))(v38 + 8))(usr_input, slicer);// slice = usr_input[i8:j8]
if ( SLODWORD(v40->refcnt) >= 0 )
{
v23 = v40->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(v40);
}
if ( !j_slice )
goto LABEL_205;
if ( SLODWORD(i8->refcnt) >= 0 )
{
v23 = i8->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(i8);
}
if ( SLODWORD(j8->refcnt) >= 0 )
{
v23 = j8->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(j8);
}
v41 = v76;
v76 = j_slice;
if ( v41 )
{
if ( SLODWORD(v41->refcnt) >= 0 )
{
v23 = v41->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(v41);
}
}
string_rand0m = table[19];
Item_KnownHash = (Py_obj *)PyDict_GetItem_KnownHash(*table, string_rand0m, string_rand0m->item);
v44 = Item_KnownHash;
if ( Item_KnownHash )
{
v45 = LODWORD(Item_KnownHash->refcnt) + 1;
if ( v45 )
LODWORD(v44->refcnt) = v45;
midd = v44;
}
else if ( PyErr_Occurred() || (midd = (Py_obj *)sub_7FFDD0C83D40(string_rand0m), (v44 = midd) == 0LL) )
{
v7 = 17;
v8 = 2941;
v4 = i;
goto LABEL_223;
}
item = 0LL;
v47 = 0;
if ( v44->type == (_QWORD *)PyMethod_Type )
{
item = (_DWORD *)v44->item;
if ( item )
{
midd = (Py_obj *)v44->size;
if ( *item != -1 )
++*item;
if ( LODWORD(midd->refcnt) != -1 )
++LODWORD(midd->refcnt);
if ( SLODWORD(v44->refcnt) >= 0 )
{
v23 = v44->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(v44);
}
v47 = 1;
}
}
v73[0] = j_slice;
res = call_rand0m((__int64)midd, &v73[-v47], (unsigned int)(v47 + 1));// res = rand0m(slice)
if ( item )
{
if ( (int)*item >= 0 )
{
v23 = (*(_QWORD *)item)-- == 1LL;
if ( v23 )
Py_Dealloc(item);
}
}
if ( !res )
{
v7 = 17;
v8 = 2961;
v4 = i;
LABEL_216:
v5 = v76;
goto LABEL_217;
}
if ( SLODWORD(midd->refcnt) >= 0 )
{
v23 = midd->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(midd);
}
v49 = v72;
v50 = res;
v72 = res;
if ( v49 )
{
if ( SLODWORD(v49->refcnt) >= 0 )
{
v23 = v49->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(v49);
}
}
j_slice = Get_Item(res, 1LL); // r2 = res[1]
if ( !j_slice )
{
v7 = 18;
v8 = 2975;
v4 = i;
goto LABEL_223;
}
v52 = (Py_obj *)i->type;
if ( v52 == PyLong_Type )
{
v53 = i->size;
if ( (v53 & 1) != 0 )
{
if ( LODWORD(i->refcnt) != -1 )
++LODWORD(i->refcnt);
i2 = i;
midd = i;
goto LABEL_114;
}
v55 = v53 >= 0x10
? (Py_obj *)(*(__int64 (__fastcall **)(Py_obj *, Py_obj *))(*((_QWORD *)&PyLong_Type + 12) + 16LL))(
i,
table[31])
: PyLong_FromLongLong((Py_obj *)(2LL * (int)(LODWORD(i->item) * (1 - (v53 & 3)))));
}
else
{
v55 = v52 == (Py_obj *)PyFloat_Type ? PyFloat_FromDouble(v51) : PyNumber_Multiply(i, table[31]);
}
i2 = v55; // i2 = 2 * i
midd = v55;
if ( !v55 )
{
v7 = 18;
v8 = 2977;
v4 = i;
goto LABEL_213;
}
LABEL_114:
k = Get_Item_list(key, i2); // k = key[i * 2]
if ( !k )
{
v7 = 18;
v8 = 2979;
v4 = i;
goto LABEL_213;
}
if ( SLODWORD(i2->refcnt) >= 0 )
{
v23 = i2->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(i2);
}
midd = PyObject_RichCompare(j_slice, k, 2LL);// if r2 == k:
if ( !midd )
{
v7 = 18;
v8 = 2982;
goto LABEL_209;
}
if ( SLODWORD(j_slice->refcnt) >= 0 )
{
v23 = j_slice->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(j_slice);
}
if ( SLODWORD(k->refcnt) >= 0 )
{
v23 = k->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(k);
}
IsTrue = midd == (Py_obj *)Py_TrueStruct;
if ( !(IsTrue | (midd == (Py_obj *)Py_FalseStruct || midd == Py_NoneStruct)) )
IsTrue = PyObject_IsTrue(midd);
if ( IsTrue < 0 )
{
v7 = 18;
v8 = 2985;
v4 = i;
goto LABEL_218;
}
if ( SLODWORD(midd->refcnt) >= 0 )
{
v23 = midd->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(midd);
}
if ( !IsTrue )
goto neq_k;
midd = Get_Item(v50, 0LL); // r1 = res[0]
if ( !midd )
{
v7 = 18;
v8 = 2992;
v4 = i;
goto LABEL_223;
}
v58 = table;
v59 = (Py_obj *)i->type;
if ( v59 != PyLong_Type )
{
if ( v59 == (Py_obj *)PyFloat_Type )
v62 = PyFloat_FromDouble(v57);
else
v62 = PyNumber_Multiply(table[31], i);
goto LABEL_144;
}
v60 = i->size;
if ( (v60 & 1) == 0 )
{
if ( v60 >= 0x10 )
v62 = (Py_obj *)(*(__int64 (__fastcall **)(Py_obj *, Py_obj *))(*((_QWORD *)&PyLong_Type + 12) + 16LL))(
table[31],
i);
else
v62 = PyLong_FromLongLong((Py_obj *)(2LL * (int)(LODWORD(i->item) * (1 - (v60 & 3)))));
LABEL_144:
v61 = v62;
k = v62;
if ( !v62 )
{
v7 = 18;
v8 = 2994;
v4 = i;
goto LABEL_218;
}
v58 = table;
goto LABEL_146;
}
if ( LODWORD(i->refcnt) != -1 )
++LODWORD(i->refcnt);
v61 = i;
k = i;
LABEL_146:
j_slice = Add(v61, v58[30]);
if ( !j_slice )
{
v7 = 18;
v8 = 2996;
LABEL_209:
v4 = i;
if ( SLODWORD(k->refcnt) >= 0 )
{
v23 = k->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(k);
}
if ( !j_slice )
goto LABEL_216;
goto LABEL_213;
}
if ( SLODWORD(v61->refcnt) >= 0 )
{
v23 = v61->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(v61);
}
k = Get_Item_list(key, j_slice); // k = key[2 * i + 1]
if ( !k )
{
v7 = 18;
v8 = 2999;
v4 = i;
goto LABEL_213;
}
if ( SLODWORD(j_slice->refcnt) >= 0 )
{
v23 = j_slice->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(j_slice);
}
j_slice = PyObject_RichCompare(midd, k, 2LL);// if res[0] == k:
if ( !j_slice )
{
v7 = 18;
v8 = 3002;
goto LABEL_209;
}
if ( SLODWORD(midd->refcnt) >= 0 )
{
v23 = midd->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(midd);
}
midd = 0LL;
if ( SLODWORD(k->refcnt) >= 0 )
{
v23 = k->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(k);
}
IsTrue_ = j_slice == (Py_obj *)Py_TrueStruct;
if ( !(IsTrue_ | (j_slice == (Py_obj *)Py_FalseStruct || j_slice == Py_NoneStruct)) )
IsTrue_ = PyObject_IsTrue(j_slice);
if ( IsTrue_ < 0 )
{
v7 = 18;
v8 = 3005;
v4 = i;
LABEL_213:
if ( SLODWORD(j_slice->refcnt) >= 0 )
{
v23 = j_slice->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(j_slice);
}
goto LABEL_216;
}
if ( SLODWORD(j_slice->refcnt) >= 0 )
{
v23 = j_slice->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(j_slice);
}
if ( IsTrue_ )
{
v64 = Add(v19, table[30]);
if ( !v64 ) // count += 1
{
v7 = 19;
v8 = 3018;
v4 = i;
goto LABEL_223;
}
v65 = v19;
v77 = v64;
v19 = v64;
if ( SLODWORD(v65->refcnt) >= 0 )
{
v23 = v65->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(v65);
}
}
neq_k: // else:
midd = 0LL;
c4 = tmp1 + 1;
v4 = i;
tmp1 = c4;
if ( c4 >= 4 )
break;
v2 = usr_input;
}
v66 = table[32];
if ( v19 == v66 )
{
v67 = (Py_obj *)Py_TrueStruct;
}
else
{
v68 = (Py_obj *)v19->type;
if ( v68 == PyLong_Type )
{
if ( (v19->size & 2) == 0 && v19->size >> 3 == 1LL && LODWORD(v19->item) == 4 )
{
v67 = (Py_obj *)Py_TrueStruct;
goto LABEL_193;
}
}
else
{
if ( v68 != (Py_obj *)PyFloat_Type )
{
v67 = PyObject_RichCompare(v19, v66, 2LL);// 最后一次比较
goto LABEL_193;
}
if ( *(double *)&v19->size == 4.0 )
{
v67 = (Py_obj *)Py_TrueStruct;
goto LABEL_193;
}
}
v67 = (Py_obj *)Py_FalseStruct;
}
LABEL_193:
v5 = v76;
if ( v67 )
{
v69 = key;
goto LABEL_226;
}
v7 = 20;
v77 = v19;
v8 = 3040;
LABEL_217:
if ( !midd )
goto LABEL_224;
LABEL_218:
if ( SLODWORD(midd->refcnt) >= 0 )
{
v23 = midd->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(midd);
}
LABEL_223:
v5 = v76;
LABEL_224:
v3 = v77;
LABEL_225:
sub_7FFDD0C86240("rand0m.check", v8, v7, (__int64)"rand0m.pyx");
v69 = key;
v19 = v3;
v50 = v72;
v67 = 0LL;
if ( key )
{
LABEL_226:
if ( SLODWORD(v69->refcnt) >= 0 )
{
v23 = v69->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(v69);
}
}
if ( v19 )
{
if ( SLODWORD(v19->refcnt) >= 0 )
{
v23 = v19->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(v19);
}
}
if ( v4 )
{
if ( SLODWORD(v4->refcnt) >= 0 )
{
v23 = v4->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(v4);
}
}
if ( v5 )
{
if ( SLODWORD(v5->refcnt) >= 0 )
{
v23 = v5->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(v5);
}
}
if ( v50 )
{
if ( SLODWORD(v50->refcnt) >= 0 )
{
v23 = v50->refcnt-- == 1LL;
if ( v23 )
Py_Dealloc(v50);
}
}
return v67;
}

Py_obj *__fastcall rand0m_rand0m(__int64 a1, Py_obj *slice)
{
Py_obj *v2; // r13
Py_obj *p1; // r14
Py_obj *v5; // rdi
Py_obj *xnum; // r12
unsigned int v7; // r15d
Py_obj *v_tuple; // rbx
unsigned int v9; // ebp
Py_obj **v10; // rdx
Py_obj *v11; // rcx
Py_obj *num; // rsi
bool v13; // zf
Py_obj *v14; // rax
Py_obj *rnum; // rax
Py_obj **v16; // rcx
unsigned __int64 size; // rdx
unsigned __int64 v18; // rcx
Py_obj *v19; // rax
Py_obj *p2; // rbp
Py_obj *v21; // rcx
Py_obj *tmp1; // rax
Py_obj *tmp2; // rsi
Py_obj *v24; // rcx
Py_obj *v25; // rax
Py_obj *_xnum; // rbx
Py_obj *v27; // rcx
Py_obj *pw; // rax
Py_obj *p3; // rsi
Py_obj *nv_tuple; // rax

v2 = 0LL;
p1 = 0LL;
v5 = 0LL;
xnum = 0LL;
v7 = 2;
v_tuple = PyTuple_New(2LL); // v = (None, None)
if ( !v_tuple )
{
v9 = 2594;
goto LABEL_77;
}
if ( LODWORD(slice->refcnt) != -1 )
++LODWORD(slice->refcnt);
v10 = table;
v_tuple->item = slice; // v[0] = slice
v11 = v10[36];
if ( LODWORD(v11->refcnt) != -1 )
++LODWORD(v11->refcnt);
v_tuple[1].refcnt = v10[36]; // v[1] = 0x10
num = sub_7FFDD0C842B0(PyLong_Type, v_tuple); // num = int(slice, 0x10)
if ( !num )
{
v9 = 2602;
LABEL_48:
if ( SLODWORD(v_tuple->refcnt) >= 0 )
{
v13 = v_tuple->refcnt-- == 1LL;
if ( v13 )
LABEL_50:
Py_Dealloc(v_tuple);
}
LABEL_77:
sub_7FFDD0C86240("rand0m.rand0m", v9, v7, (__int64)"rand0m.pyx");
if ( !v5 )
goto LABEL_87;
goto LABEL_84;
}
if ( SLODWORD(v_tuple->refcnt) >= 0 )
{
v13 = v_tuple->refcnt-- == 1LL;
if ( v13 )
Py_Dealloc(v_tuple);
}
v5 = num;
v14 = (Py_obj *)PyNumber_Xor(num, table[44]);
if ( !v14 )
{
v9 = 2615;
v7 = 3;
goto LABEL_77;
}
xnum = v14; // xnum = num ^ 0x9e3779b9
rnum = Rshift(num, table[0x21], 5, 0); // rnum = num >> 5
if ( !rnum )
{
v9 = 2627;
v7 = 4;
goto LABEL_77;
}
v16 = table;
p1 = rnum;
if ( (Py_obj *const)num->type != PyLong_Type )
goto LABEL_32;
size = num->size;
if ( (size & 1) != 0 )
{
if ( LODWORD(num->refcnt) != -1 )
++LODWORD(num->refcnt);
v_tuple = num;
goto LABEL_36;
}
if ( size >= 0x10 )
{
switch ( (size >> 3) * (1 - (num->size & 3LL)) )
{
case 0xFFFFFFFFFFFFFFFEuLL:
v18 = -(__int64)(LODWORD(num->item) | ((unsigned __int64)HIDWORD(num->item) << 30));
goto LABEL_29;
case 2uLL:
v18 = LODWORD(num->item) | ((unsigned __int64)HIDWORD(num->item) << 30);
goto LABEL_29;
default:
v19 = (Py_obj *)(*(__int64 (__fastcall **)(Py_obj *, Py_obj *))(*((_QWORD *)&PyLong_Type + 12) + 88LL))(
num,
table[32]);
break;
}
goto LABEL_33;
}
LODWORD(v18) = LODWORD(num->item) * (1 - (size & 3));
if ( (_DWORD)v18 != (16 * (int)v18) >> 4 && (_DWORD)v18 )
{
v18 = (int)v18;
LABEL_29:
if ( v18 == (__int64)(0x10 * v18) >> 4 )
{
v19 = PyLong_FromLongLong((Py_obj *)(0x10 * v18));
goto LABEL_33;
}
LABEL_32:
v19 = (Py_obj *)PyNumber_Lshift(num, table[32]);
goto LABEL_33;
}
v19 = PyLong_FromLong((unsigned int)(16 * v18));
LABEL_33:
num = v19;
v_tuple = v19;
if ( !v19 )
{
v9 = 2639;
v7 = 5;
goto LABEL_77;
}
v16 = table;
LABEL_36:
p2 = PyNumber_And(num, v16[48]); // p2 = (num << 4) & 0xfa3affff
if ( !p2 )
{
v9 = 2641;
v7 = 5;
LABEL_66:
if ( SLODWORD(v_tuple->refcnt) >= 0 )
{
v13 = v_tuple->refcnt-- == 1LL;
if ( v13 )
goto LABEL_50;
}
goto LABEL_77;
}
if ( SLODWORD(num->refcnt) >= 0 )
{
v13 = num->refcnt-- == 1LL;
if ( v13 )
Py_Dealloc(num);
}
v21 = v5;
v5 = p2;
if ( SLODWORD(v21->refcnt) >= 0 )
{
v13 = v21->refcnt-- == 1LL;
if ( v13 )
Py_Dealloc(v21);
}
tmp1 = Rshift(p1, table[37], 0x17, 0); // (tmp1 = rnum >> 0x17)
v_tuple = tmp1;
if ( !tmp1 )
{
v9 = 2654;
v7 = 6;
goto LABEL_77;
}
tmp2 = PyNumber_Add(p2, tmp1); // (tmp2 = tmp1 + p2)
if ( !tmp2 )
{
v9 = 2656;
v7 = 6;
goto LABEL_48;
}
if ( SLODWORD(v_tuple->refcnt) >= 0 )
{
v13 = v_tuple->refcnt-- == 1LL;
if ( v13 )
Py_Dealloc(v_tuple);
}
v24 = p1;
p1 = tmp2; // p1 = tmp2
if ( SLODWORD(v24->refcnt) >= 0 )
{
v13 = v24->refcnt-- == 1LL;
if ( v13 )
Py_Dealloc(v24);
}
v25 = Rshift(xnum, table[35], 0xB, 1);
_xnum = v25;
if ( !v25 )
{
v9 = 2669;
v7 = 7;
goto LABEL_77;
}
v27 = xnum;
xnum = v25; // xnum >>= 0xb
if ( SLODWORD(v27->refcnt) >= 0 )
{
v13 = v27->refcnt-- == 1LL;
if ( v13 )
Py_Dealloc(v27);
}
pw = PyNumber_Power(_xnum, table[38], Py_NoneStruct);// (pw = xnum ** 0x10001)
v_tuple = pw;
if ( !pw )
{
v9 = 2681;
v7 = 8;
goto LABEL_77;
}
p3 = PyNumber_Remainder(pw, table[49]); // p3 = pw % 0x33FFFFFFD
if ( !p3 )
{
v9 = 2683;
v7 = 8;
goto LABEL_66;
}
if ( SLODWORD(v_tuple->refcnt) >= 0 )
{
v13 = v_tuple->refcnt-- == 1LL;
if ( v13 )
Py_Dealloc(v_tuple);
}
v5 = p3;
if ( SLODWORD(p2->refcnt) >= 0 )
{
v13 = p2->refcnt-- == 1LL;
if ( v13 )
Py_Dealloc(p2);
}
nv_tuple = PyTuple_New(2LL);
if ( !nv_tuple )
{
v9 = 2697;
v7 = 9;
goto LABEL_77;
}
if ( LODWORD(p3->refcnt) != -1 )
++LODWORD(p3->refcnt);
nv_tuple->item = p3;
if ( LODWORD(p1->refcnt) != -1 )
++LODWORD(p1->refcnt);
nv_tuple[1].refcnt = p1;
v2 = nv_tuple;
LABEL_84:
if ( SLODWORD(v5->refcnt) >= 0 )
{
v13 = v5->refcnt-- == 1LL;
if ( v13 )
Py_Dealloc(v5);
}
LABEL_87:
if ( xnum )
{
if ( SLODWORD(xnum->refcnt) >= 0 )
{
v13 = xnum->refcnt-- == 1LL;
if ( v13 )
Py_Dealloc(xnum);
}
}
if ( p1 )
{
if ( SLODWORD(p1->refcnt) >= 0 )
{
v13 = p1->refcnt-- == 1LL;
if ( v13 )
Py_Dealloc(p1);
}
}
return v2; // return (p3, p1)
}

check函数开头初始化的key表是乱的 这里用脚本来获取其中的数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from idc import get_bytes

def fromPyLong(PyobjAddr):
size = get_bytes(PyobjAddr + 8 * 2, 8)
size = int.from_bytes(size, 'little')
digtsLen = (size // 8)
digtsAddr = PyobjAddr + 8 * 3
pynum = 0
for i in range(digtsLen):
t = int.from_bytes(get_bytes(digtsAddr + i * 4, 4), 'little') & 0x3FFFFFFF
pynum |= t << (30 * i)

return pynum

table = 0x212827009B0
datas = []
for i in range(8):
solved_addr = int.from_bytes(get_bytes(table + i * 8, 8), 'little')
data = fromPyLong(solved_addr)
datas.append(data)

print(", ".join(map(hex, datas)))

还原过后就能直接用Z3解出符合条件的输入了:

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
from z3 import *
import rand0m

key = [0x12287f38, 0x98d24b3a, 0x4a30f74d, 0xe0f1db77, 0x23a1268, 0xadf38403, 0x88108807, 0xd8499bb6]
usr_input = ""
count = 0

# def rand0m(slc):
# num = int(slc, 16)
# xnum = num ^ 0x9e3779b9
# p2 = (num << 4) & 0xfa3affff
# p1 = ((num >> 5) >> 0x17) + p2
# xnum >>= 0xB
# p3 = (xnum ** 0x10001) % 0xFFFFFFFD
# return (p3, p1)

# for i in range(4):
# _slice = usr_input[i * 8: (i + 1) * 8]
# res = rand0m(_slice)
# k = key[i * 2]
# if res[1] == k:
# k = key[i * 2 + 1]
# if res[0] == k:
# count += 1

# if count == 4:
# print("Correct!")

flag = ""

for p in range(4):
s = Solver()
num = BitVec(f'num_{p}', 33)
xnum = num ^ 0x9e3779b9
p2 = (num << 4) & 0xfa3affff
p1 = ((num >> 5) >> 23) + p2
s.add(p1 == key[p * 2])
while s.check() == sat:
m = s.model()
pw = rand0m.rand0m(hex(m[num].as_long())[2:])
if pw[0] == key[p * 2 + 1]:
flag += hex(m[num].as_long())[2:]
print(flag)
break
s.add(num != m[num])

PyArmor

没学到

2d02c83d1e65f2f5d83a2289822185cd