bad_python

攻防世界 (xctf.org.cn)

前戏

下载文件,解压完成后是这个

bad_python_第1张图片

一个pyc文件

这里要用到python的反编译

要用到的工具有两个

1.python自带的uncompyle6

2.pycdc文件——比uncompyle6强大一点

我们一个一个来尝试一下

uncompyle6:

bad_python_第2张图片

我是直接在pycharm里面终端用的

把文件放在这个目录下

bad_python_第3张图片

你需要先安装

然后输入这个命令(在cmd里面也行)

一般来说就OK了

但是这道题考点是进一步的

bad_python_第4张图片

这是运行了,但是运行出错

最后得到的这个py文件是0kb的,什么都没有

至于后续,我先讲另一种方法,比较常用的

pycdc:

首先你要去下载一个pycdc文件(52上面有)

这样输入,你就会得到一个py文件,名字叫做1.py

但是是0kb

bad_python_第5张图片

修改文件头

这个MAGIC是指pyc的文件头是坏的

pyc的文件头,我用010editor给大家打开看一下

bad_python_第6张图片

蓝色标记这部分就是pyc文件的文件头

它包含的信息包括:

前四个字节——版本信息,比如python的3.6,3.8等

中间8个字节——不用管,修改时间而已,对我们来说没有

后面四个字节——包含大小信息和校验码(我也不确定,反正有用)

一般这种错误就是因为这个文件的文件头信息被修改了,我们需要去修复,也就是将文件头修复正确中间我们最重要的一步就是找到他是什么版本的pyc文件,不同版本的文件头不同

我们先给出答案,这道题的版本是3.6,我们可以从两个地方看出来

一:文件名字

还记得最开始文件名那么长的字符吗,你是不是就下意识忽略了,我们在回过头看一下(当时我也没看出来)

3.6版本

二:文件头

这个33 0D 就是3.6版本的文件名开头,他只是修改了后面的部分

然后下面就是我们找到正确的3.6版本的文件头是怎样的了

我是直接百度的,当然厉害的就是自己用3.6版本的python写一个pyc文件,放在010里面去看

bad_python_第7张图片

橘黄色的就是改的

bad_python_第8张图片

现在跑出来的py文件就有3KB了

后面就算正常的IDA静态分析了

IDA分析

因为这是个py文件,我们可以直接放入python中去看,他也只有3KB,代码不是很多,就不麻烦IDA了,你要放在IDA里面去看也行

# Source Generated with Decompyle++
# File: 1.pyc (Python 3.6)

from ctypes import *
from Crypto.Util.number import bytes_to_long
from Crypto.Util.number import long_to_bytes

def encrypt(v, k):
    v0 = c_uint32(v[0])
    v1 = c_uint32(v[1])
    sum1 = c_uint32(0)
    delta = 195935983
    for i in range(32):
        v0.value += (v1.value << 4 ^ v1.value >> 7) + v1.value ^ sum1.value + k[sum1.value & 3]
        sum1.value += delta
        v1.value += (v0.value << 4 ^ v0.value >> 7) + v0.value ^ sum1.value + k[sum1.value >> 9 & 3]
    
    return (v0.value, v1.value)

if __name__ == '__main__':
    flag = input('please input your flag:')
    k = [
        255,
        187,
        51,
        68]
    if len(flag) != 32:
        print('wrong!')
        exit(-1)
    a = []
    for i in range(0, 32, 8):
        v1 = bytes_to_long(bytes(flag[i:i + 4], 'ascii'))
        v2 = bytes_to_long(bytes(flag[i + 4:i + 8], 'ascii'))
        a += encrypt([
            v1,
            v2], k)
    
    enc = [
        0xEEC7D402L,
        0x99E9363FL,
        0x853BDE61L,
        558171287,
        0x908F94B0L,
        1715140098,
        986348143,
        1948615354]
    for i in range(8):
        if enc[i] != a[i]:
            print('wrong!')
            exit(-1)
    print('flag is flag{%s}' % flag)

bad_python_第9张图片

就是这么个事

直接开干吧

这段代码可能新手比较懵逼

这个<<4,就是在二进制表示下,把整个数往左边移动4位,就也是变大了2的四次方大

这个>>7就是右移7次,就是表现2的七次方小,向下取整

然后这个&3就是在二进制层面按位与

3是11,8是11000

1000

0011

相同位置如果都是1,那么得到1,不同为0

得到的结果就是

0000

对应这段代码我很懵逼

然后我去实验了下

bad_python_第10张图片

825307441

然后把1111又换成了aaaa,也是一串数字

得到了这个数字,然后感觉就是把4个字符串转换成为了一个数字,感觉和base64作用差不多

就是把每个字符的ASCII码提出来,然后通过一些算法组合在一起

这里我采用的就是暴力的方法的,硬跑

加上这个直接全部遍历一遍

(抄一个吧,不想写了)

我是废物

enc = [63912563639333235,63912563820295007,63912563319981409,63912564160552784,63912564260890672,63912564074962770,63912563086615890,63912564159242032]
flag = bytearray(32)
for i in range(8):
    flag[4 * i] = (enc[i] >> 24) & 0xFFFFFFFF & 0xFF
    flag[4 * i + 1] = (enc[i] >> 16) & 0xFFFFFFFF & 0xFF
    flag[4 * i + 2] = (enc[i] >> 8) & 0xFFFFFFFF & 0xFF
    flag[4 * i + 3] = (enc[i] & 0xFFFFFFFF & 0xFF)

print(flag)
#bytearray(b'Th1s_1s_A_Easy_Pyth0n__R3veRse_0')

看不懂为啥这样就OK了,我是废物!~

但是废物也要下班拉~

你可能感兴趣的:(python,开发语言,网络安全,密码学,数据结构)