PS:由于本人没系统学过python,所以只会逆向里常见的函数什么。。
判断 大部分python编辑的exe文件很好识别,或者我们拖入die中(ELF可以采取这招),可以看到它是什么语言写的 比如 打包工具pyinstaller是一个比较常见的py打包工具 通常我们拿到的附件就是exe或者elf了
拿到这个文件,我们怎么反编译捏 这时候需要用到两个工具
解包 一个是解包用的pyinstxtractor.py pyinstxtractor.py可以在github上找到 使用的时候需要我们把它放到我们解包的同一目录下,在终端中执行
1 python pyinstxtractor.py 文件名
解包完之后我们得检查一下struct.pyc和(我们解包的文件名).pyc前两行文件头是否一样(有时候出题人会在这里挖坑) 如果不一样需要把(文件名).pyc的改成struct.pyc 然后反编译
反编译 uncompyle6适用python2.7到python3.9
1 uncompyle6 文件.pyc > 文件名.py
或者
1 uncompyle6 -o (希望文件输出的地址) 文件.pyc
pycdc适用python3.9以上版本 使用方法和uncompyle6差不多 这两个工具基本都会用到
然后就可以逆了 嗯嗯就是正常逆向了
一些例题 [isctf]ELF 进行常规解包,这是一个elf, 用pycdc进行反编译得到原码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import base64import hashlibimport randomflag = '8d13c398b72151b1dad78762553dbbd59dba9b0b2330b03b401ea4f2a6d4731d479220fe900b520f6b4753667fe1cdf9eff8d3b833a0013c4083fa1ad27d056486702bda245f3c1aa0fbf84b237d8f2dec9a80791fe66625adfe3669419a104cbb67293eaada20f79cebf69d84d326025dd35dec09a2c97ad838efa5beba9e72' def Rep (hash_data ): random.seed(161 ) result = list (hash_data) for i in range (len (result) - 1 , 0 , -1 ): swap_index = random.randint(0 , i) result[i] = result[swap_index] result[swap_index] = result[i] return '' .join(result) for i in range (len (YourInput) // 3 ): c2b = base64.b64encode(YourInput[i * 3 :(i + 1 ) * 3 ].encode('utf-8' )) hash = hashlib.md5(c2b).hexdigest() enc += Rep(hash ) if enc == flag: print ('Your are win!!!' ) return None None ('Your are lose!!!' )
哇塞,py居然有内置的base64和MD5 注意这里
1 2 result[i] = result[swap_index] result[swap_index] = result[i]
这里实际上是反编译错误的问题,如果逻辑真的是这样,result[i]中的值会被覆盖 正确逻辑是
1 result[i],result[swap_index]= result[swap_index],result[i]
(这个代码好多地方我看懂了但是没知道该怎么逆。。。写一下心路历程) 先把input用base64加密然后再MD5加密然后自定义加密 本来想把rep逆一下的,但是考虑到MD5只能爆破,都要爆破了那再加密一遍爆破也可以 (正常爆破:MD5(遍历的值)== 加密后的值(rep逆向过来的值) 但是也可以:rep(MD5(遍历的值)== 最终的加密结果) python也有内置的解密base64函数 MD5爆破范围在ascii码壳打印范围里 然后得到解密脚本
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 import base64import hashlibimport randomflag = '8d13c398b72151b1dad78762553dbbd59dba9b0b2330b03b401ea4f2a6d4731d479220fe900b520f6b4753667fe1cdf9eff8d3b833a0013c4083fa1ad27d056486702bda245f3c1aa0fbf84b237d8f2dec9a80791fe66625adfe3669419a104cbb67293eaada20f79cebf69d84d326025dd35dec09a2c97ad838efa5beba9e72' def Rep (hash_data ): random.seed(161 ) result = list (hash_data) for i in range (len (result) - 1 , 0 , -1 ): swap_index = random.randint(0 , i) result[i] ,result[swap_index] = result[swap_index],result[i] return '' .join(result) ans=[] asc='' .join(chr (i)for i in range (32 ,127 )) blocks = [flag[i:i+32 ] for i in range (0 , len (flag), 32 )] for i in blocks: found = None for a in asc: for b in asc: for c in asc: s =a+b+c b64 = base64.b64encode(s.encode('ascii' )) w= hashlib.md5(b64).hexdigest() if i == Rep(w): found = s break if found: break if found: break ans.append(found) print (ans)print ('' .join(ans))
[isctf]ezpy 引入了一个自定义库 这个题也是用比较高的python版本写的,用pycdc反编译。但是报错(
可能是自定义库的问题 然后用pycdas进行反汇编,然后读汇编
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 ezpy.pyc (Python 3.13) [Code] File Name: ezpy.py Object Name: <module> Qualified Name: <module> Arg Count: 0 Pos Only Arg Count: 0 KW Only Arg Count: 0 Stack Size: 2 Flags: 0x00000000 [Names] 'mypy' 'check' 'ImportError' 'print' 'exit' 'main' '__name__' [Locals+Names] [Constants] 0 ( 'check' ) 'Error: Cannot import mypy module' 1 [Code] File Name: ezpy.py Object Name: main Qualified Name: main Arg Count: 0 Pos Only Arg Count: 0 KW Only Arg Count: 0 Stack Size: 3 Flags: 0x00000003 (CO_OPTIMIZED | CO_NEWLOCALS) [Names] 'input' 'strip' 'check' 'print' [Locals+Names] 'user_input' [Constants] None 'Please input your flag: ' 'Correct!' 'Wrong!' [Disassembly] 0 RESUME 0 2 LOAD_GLOBAL 1: NULL + input 12 LOAD_CONST 1: 'Please input your flag: ' 14 CALL 1 22 LOAD_ATTR 3: strip 42 CALL 0 50 STORE_FAST 0: user_input 52 LOAD_GLOBAL 5: NULL + check 62 LOAD_FAST 0: user_input 64 CALL 1 72 TO_BOOL 80 POP_JUMP_IF_FALSE 12 (to 106) 84 LOAD_GLOBAL 7: NULL + print 94 LOAD_CONST 2: 'Correct!' 96 CALL 1 104 POP_TOP 106 RETURN_CONST 0: None 108 LOAD_GLOBAL 7: NULL + print 118 LOAD_CONST 3: 'Wrong!' 120 CALL 1 128 POP_TOP 130 RETURN_CONST 0: None [Exception Table] '__main__' None [Disassembly] 0 RESUME 0 2 NOP 4 LOAD_CONST 0: 0 6 LOAD_CONST 1: ('check',) 8 IMPORT_NAME 0: mypy 10 IMPORT_FROM 1: check 12 STORE_NAME 1: check 14 POP_TOP 16 LOAD_CONST 4: <CODE> main 18 MAKE_FUNCTION 20 STORE_NAME 5: main 22 LOAD_NAME 6: __name__ 24 LOAD_CONST 5: '__main__' 26 COMPARE_OP 88 (==) 30 POP_JUMP_IF_FALSE 8 (to 48) 34 LOAD_NAME 5: main 36 PUSH_NULL 38 CALL 0 46 POP_TOP 48 RETURN_CONST 6: None 50 RETURN_CONST 6: None 52 PUSH_EXC_INFO 54 LOAD_NAME 2: ImportError 56 CHECK_EXC_MATCH 58 POP_JUMP_IF_FALSE 19 (to 98) 62 POP_TOP 64 LOAD_NAME 3: print 66 PUSH_NULL 68 LOAD_CONST 2: 'Error: Cannot import mypy module' 70 CALL 1 78 POP_TOP 80 LOAD_NAME 4: exit 82 PUSH_NULL 84 LOAD_CONST 3: 1 86 CALL 1 94 POP_TOP 96 POP_EXCEPT 98 JUMP_BACKWARD_NO_INTERRUPT 42 (to 16) 100 RERAISE 0 102 COPY 3 104 POP_EXCEPT 106 RERAISE 1 [Exception Table] 4 to 16 -> 52 [0] 52 to 96 -> 102 [1] lasti 100 to 102 -> 102 [1] lasti
其实还是很清晰的,check的过程应该都在mypy里,这里没什么别的加密,只是普通输入和输出 将文件夹中的”mypy.cp313-win_amd64.pyd”用ida打开 依旧字符串窗口找关键字 合理推测sub_36F4D1519函数(图中黄色)
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 __m128i *__fastcall sub_36F4D1519 (__int64 a1, __int64 a2) { char *v2; __m128i *v3; __m128i *v5; unsigned int v6; __int64 v7; char v8[274 ]; char *Str; strcpy (v8, "ISCTF2025" ); if ( !PyArg_ParseTuple(a2, &unk_36F4D4000, &Str) ) return 0 i64; v2 = Str; v3 = Py_FalseStruct; if ( strlen (Str) == 25 ) { v5 = malloc (0x19u i64); v3 = v5; if ( v5 ) { *v5 = _mm_loadu_si128(v2); *(v5 + 9 ) = _mm_loadu_si128((v2 + 9 )); v6 = strlen (v8); sub_36F4D1430(&v8[10 ], v8, v6); sub_36F4D149C(&v8[10 ], v3, 25 i64); v7 = 0 i64; while ( v3->m128i_i8[v7] == byte_36F4D4050[v7] ) { if ( ++v7 == 25 ) { free (v3); return Py_TrueStruct; } } free (v3); return Py_FalseStruct; } else { PyErr_NoMemory(); } } return v3; }
key是ISCTF2025 加密数据在byte_36F4D4050里1DD53833AFB551F32C6B6EFE412443D271CFA44CE39A9AB531 用在线解密工具
z3约束 以前一直没仔细学,这下正好顺手精进一下(bushi (我并不会py,这里可能会掺杂一些c里面的概念,还请谅解 z3 要先
使用时要记得
1 2 3 4 5 from z3 import *变量名=Solver() 变量名.add() 变量名.check() 变量名.model()
主要是向量的初始化一直不会,然后怎么print出我想要的东西
1 2 3 4 5 6 7 8 9 10 x = BitVec('x' , 8 ) y = BitVec('y' , 8 ) a = BitVecVal(10 , 8 ) b = BitVecVal(0xFF , 8 ) a= [BitVec(f'a{i} ' , 8 )for i in range (数字)]
想要print出来,就
1 2 3 4 5 6 7 8 if (check返回值) == sat: m = 变量名.model() flag_bytes = [m.evaluate(raw_a1[i]).as_long() for i in range (数字)] try : flag = '' .join(chr (b) for b in flag_bytes) except : flag = repr (flag_bytes) print (flag)
晚安