[2020 GeekChallenge] Re un_snake

3 篇文章 0 订阅

[2020 GeekChallenge] Re un_snake

0x00 逆向

下载文件后,发现是pyc文件。
pyc文件可以使用uncompyle6库。题目告诉我们是3.8.5版本的python,因此我就给python3装了库。

pip3 install uncompyle6

注意我电脑装的是python2 python3共存,如果电脑只有python3的直接pip就行,不用pip3。
装完以后,去下载文件路径里,打开cmd,输入

uncompyle6 -o un_snake.cpython-38.py un_snake.cpython-38.pyc

就成功反编译啦,反编译的文件可以在同目录下找到。
把得到的py文件用vscode打开

# uncompyle6 version 3.7.4
# Python bytecode 3.8 (3413)
# Decompiled from: Python 3.8.4 (tags/v3.8.4:dfa645a, Jul 13 2020, 16:46:45) [MSC v.1924 64 bit (AMD64)]
# Embedded file name: ./un_snake.py
# Compiled at: 2020-08-05 16:20:40
# Size of source mod 2**32: 1238 bytes
import this
from base64 import *

def pre(data):
    th1s = 'TBESCFSRSAEUITANAIIN'.encode()
    if (data_len := len(data)) > (th1s_len := len(th1s)):
        th1s = th1s * (data_len // th1s_len) + th1s[:data_len - th1s_len]
    return bytes(map(lambda x, y: x ^ y, data, th1s))


def enc(plain):
    plain = list(plain)
    plain = plain[::-1]
    for i in range(len(plain)):
        c = plain[i]
        plain[i] = (c << 3 | c >> 5) & 255
    else:
        for i in range(len(plain) - 1):
            plain[i] ^= plain[(i + 1)]
        else:
            return bytes(plain)


def check(a):
    return b64encode(a) == b'mEiQCAjJoXJy2NiZQGGQyRm6IgHYQZAICKgowHHo4Dg='


if __name__ == '__main__':
    print()
    while True:
        stuff = input('Now input you flag:')
        stuff_ready = pre(stuff.encode())
        result = check(enc(stuff_ready))
        if result:
            print('You get it! Python is so charming right?')
            break
        else:
            print('Failed, try again!')

    print('[🐍] Commit you flag, see you next time!')

得到这样一段代码
分析代码可以得到它是把我们输入的flag通过几层加密与密文比对。
我们输入的flag经过的加密函数有:pre(),enc(),check里面的b64encode()
让我们逐个分析

0x01 加密函数

pre()

def pre(data):
    th1s = 'TBESCFSRSAEUITANAIIN'.encode()
    if (data_len := len(data)) > (th1s_len := len(th1s)):
        th1s = th1s * (data_len // th1s_len) + th1s[:data_len - th1s_len]
    return bytes(map(lambda x, y: x ^ y, data, th1s))

if中的内容是:如果输入的flag长度超过了th1s,就在后面加一截,补全长度。
可以使用调试看一下效果。
在这里插入图片描述
在这里插入图片描述

可以看到th1s变成了TBESCFSRSAEUITANAIIN + TBESCFS
简单地说就是一直复读复读,直到长度和输入的flag相同。

bytes(map(lambda x, y: x ^ y, data, th1s))

然后就是加密部分,这一段其实很好理解,就是把flag和th1s每个字符互相异或。
————————————————————————————————————
虽然是后来才想到的,仔细一想,好像这个函数……是可逆的,也就是说……
a == pre(pre(a)) 是成立的。

enc()

def enc(plain):
    plain = list(plain)
    plain = plain[::-1]
    for i in range(len(plain)):
        c = plain[i]
        plain[i] = (c << 3 | c >> 5) & 255
    else:
        for i in range(len(plain) - 1):
            plain[i] ^= plain[(i + 1)]
        else:
            return bytes(plain)

我一开始还对这个for…else疑惑了很久,查了以后发现这else和没有是一样的= =。
它的规则是,for如果不是通过break结束,就会执行else,否则不执行。

从上往下分析一下

plain = plain[::-1]

这是简单地切片倒序,不懂的可以去看一下python的切片操作。

 for i in range(len(plain)):
        c = plain[i]
        plain[i] = (c << 3 | c >> 5) & 255

这一步是把每一位字符的二进制数进行位操作
后面的&255 即 & 11111111就是把二进制数取后8位,也就是说这个数字不会超过255。
前面的位操作是把二进制数c 左移三位和右移五位互相按位或。
因为只有8位,左移三位就是把原来的数左边3个数去掉,右边补3个0,
同样右移5位就是把右边5个数去掉,左边补5个0。
它们互相按位或的结果就相当于原来的数左边三位和右边5位交换位置。
所以它的逆运算就是

for i in range(len(plain)):
        c = plain[i]
        plain[i] = (c << 5 | c >> 3) & 255

然后看下一个循环

for i in range(len(plain) - 1):
            plain[i] ^= plain[(i + 1)]

这个就是很简单的按位与或,因为是从前往后与或,所以解密需要从后往前异或
逆运算:

for i in range(len(plain) - 2,-1,-1):
            plain[i] ^= plain[(i + 1)]

解密的碎片已经集齐,就差把它们组合起来了。
(千万不要像我一样还按照原来的顺序解密,我TM还在这思考了半天到底哪里有问题。)

解密函数:

def dec(plain):
    plain = list(plain)
    for i in range(len(plain) - 2,-1,-1):
        plain[i] ^= plain[(i + 1)]
    for i in range(len(plain)):
        c = plain[i]
        plain[i] = (c << 5 | c >> 3) & 255
    plain = plain[::-1]
    return bytes(plain)

b64encode()

这就是很简单的base64库函数了,百度一下就可以得到解密函数b64decode()

好了,所有的加密函数已经得到解密方法,冲!

0x02解密脚本

from base64 import *

def pre(data):
    th1s = 'TBESCFSRSAEUITANAIIN'.encode()
    data_len = len(data)
    th1s_len = len(th1s)
    if (data_len) > (th1s_len):
        th1s = th1s * (data_len // th1s_len) + th1s[:data_len - th1s_len]
    return bytes(map(lambda x, y: x ^ y, data, th1s))

def dec(plain):
    plain = list(plain)
    for i in range(len(plain) - 2,-1,-1):
        plain[i] ^= plain[(i + 1)]
    for i in range(len(plain)):
        c = plain[i]
        plain[i] = (c << 5 | c >> 3) & 255
    plain = plain[::-1]
    return bytes(plain)

result = b'mEiQCAjJoXJy2NiZQGGQyRm6IgHYQZAICKgowHHo4Dg='
print(pre(dec(b64decode(result))))

运行即可得到flag:SYC{ssssss_Th1s_is_Pyth0n_ssss~}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值