攻防世界:bad_python anaconda tea加解密 pyc的16进制头部分内容 pyc反编译成py文件的方法

这是一道难度为一的题目。看题目就知道大致内容了,这个pyc文件是破损的,你能帮我恢复吗?

        首先pyc文件里面存放的是字节码,python是一门解释型语言,他会先把.py文件转化成字节码,字节码存储在内存当中的PyCodeObject对象中,然后交给字节码虚拟机,由虚拟机一条一条的执行字节码指令。在程序解释时,Python解释器会把PyCodeObject写入.pyc文件中,在磁盘中保存,当这个程序再次运行时,就会先在磁盘中找,这样就变快了。这个原理就是这样,我们只需要知道,这个pyc文件是字节码文件,我们是无法直接看懂里面的内容的。

        回到这个文件

                        ​​​​​​​        

        看到后面是-36,这个应该是python3.6环境下的,我们可以丢进kali里面用file命令语句来看看有什么内容,如果是出现了python 3.6 byte-compiled这样的字眼,我们可以知道,这是在3.6环境下的完整的pyc文件。我们这道题目是不完整的,所以会出现这样data这样的字眼

        

        那么我们就要修复文件喽,pyc文件的恢复基本是文件头部的Magic部分(就看前16个字节就好),呃,我遇到的题目都是这样,目前来说。所以我就顺着这个思路走下去。这是一个python3.6环境的文件,每个不停版本的python,他的头都是不一样的。一般来说,前2个字节是版本信息,第3到第4是0D和0A,第5到第8是标志位,决定后面的9到16是什么意思。如果5到8是零的话,那么第9到12是时间戳,13到16是文件大小。

        我们用010editor查看文件,可以看到,这里面前16个文件,很多都是零。那么我们就要知道一个正常的pyc的前16个字节是什么样子的。我这边想到的办法是,直接在对应版本自己写一个python文件再转化为pyc文件,查看头。但是题目有很多,这种python版本也有很多。我直接用的是anaconda来帮我管理python环境,有一篇博客讲的就很清楚,我引用在文章最后面。

        这是我写的一个打印出“hello”的python文件,是在3.6环境下的。让我们在cmd中python -m py_compile 文件名.py,就会出现一个__pycache__文件夹,里面有你的pyc文件。我们在010editor中打开这个文件。

        前16字节明显完整多了,这边的话,其实把我这前16个字节全部换过去就行了的,我也试了,只要前4个字节和后面4个字节(在前16字节之间)和我这个一样就可以编译出来。

 1.用uncompyle6:可以找找博客,这是在githup上面下载的。

           直接uncompyle6.exe 文件名.pyc 这是直接在cmd中输出.py文件

           或者uncompyle6.exe 文件名.pyc > 1.py 就会在本文件夹中出现一个1.py的文件

2.用pycdc:这边建议可以在kali上面安装,就会方便很多。如果是windows比较麻烦。

            pycdc的使用就很简单了  直接./pycdc 文件名.pyc就可以了,就会出现一个.py文件

        

# uncompyle6 version 3.9.1
# Python bytecode version base 3.6 (3379)
# Decompiled from: Python 3.8.2rc2 (tags/v3.8.2rc2:777ba07, Feb 18 2020, 09:11:15) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: pyre.py
# Compiled at: 2022-10-15 15:36:44
# Size of source mod 2**32: 609 bytes
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 = [
     4006073346L, 2582197823L, 2235293281L, 558171287, 2425328816L, 
     1715140098, 986348143, 1948615354]
    for i in range(8):
        if enc[i] != a[i]:
            print("wrong!")
            exit(-1)

    print("flag is flag{%s}" % flag)

# okay decompiling pyre.cpython-36.pyc

       这就是.py文件里面的样子。encrypt这个函数,就很明显了,有delta,有循环有异或,以及k列表里面刚刚好有4个数,可以确定是tea加解密了,如果看不出来的,需要去搜索这个加密,了解了才能知道。我们应该读完这段代码,就是有一定细节(反正我错了)要注意。这是3.6环境的python代码,所以L结尾的数字是以4字节int类型的。还有就是那两个有“ascii”的地方,由于bytes_to_long这成了大端模式,我们正常的python语言里用的是小端,这样反过来计算的话,我们在写脚本的时候要切换,或者要逆序得到的数据,这样才能出现正常的字符,不然会出现乱码。这边只是吧数组中的元素进行了大小端切换,所以我们只需要把元素进行逆序就好了。

       然后就是语言的选择,我们既然以及知道要逆序代码, 数组中的元素每个都是int类型的4个字节,我们要的是字符串类型,也就是字符的这个char是一个字节,如果用的python这部分有点难处理,因为python很自由,内存不太严谨,所以我们选择c语言

#include <stdio.h>
#include<stdint.h>
void tea_my(uint32_t* v, uint32_t* k) {
	uint32_t v0 = v[0];
	uint32_t v1 = v[1];
	uint32_t delta = 195935983;
	uint32_t sum = delta * 32;
	for (int i = 0; i < 32; i++)
	{
		v1 -= (v0 << 4 ^ v0 >> 7) + v0 ^ sum + k[sum >> 9 & 3];
		sum -= delta;
		v0 -= (v1 << 4 ^ v1 >> 7) + v1 ^ sum + k[sum & 3];
	}
	v[0] = v0;
	v[1] = v1;
}

int main()
{
	uint32_t k[4] = { 255,187,51,68 };
	uint32_t enc[8] = { 4006073346, 2582197823, 2235293281, 558171287, 2425328816,
		1715140098, 986348143, 1948615354 };
	for (int i = 0; i < 8; i+=2)
	{
		tea_my(&enc[i], k);
	}
	char flag[32] = { 0 };
	for (int i = 0; i < 8; i++)
	{
		flag[4 * i] = enc[i] >> 24;
		flag[4 * i + 1] = (enc[i] >> 16) & 0xff;
		flag[4 * i + 2] = (enc[i] >> 8) & 0xff;
		flag[4 * i + 3] = (enc[i]) & 0xff;
	}
	for (int i = 0; i < 32; i++)
	{
		printf("%c", flag[i]);
	}
}






        对于函数部分,我们直接反着写就好了,tea语言的解密,主要是k数组里面的4个元素,还有enc中的元素,最重要的是传给函数tea_my的第一个参数,必须要是可以获取两个无符号的4字节的数,我们通篇也在使用uint32_t这个类型,函数里面异或的部分虽然复杂,但是完全不用看懂,直接抄就好了,后面的flag的值的取得就是通过>>操作符实现字节层面的逆序,这个在python中实现就有点困难了,最后代码运行,就可以得到flag了。

flag{Th1s_1s_A_Easy_Pyth0n__R3veRse_0}

anaconda的安装和使用(管理python环境看这一篇就够了)-优快云博客

### Anaconda 安装过程中的文件提取位置与结构 在 Anaconda 的安装流程中,`product.img` 文件是一个重要的组部分。为了测试或替换某些特定文件(如插件),可以按照以下方法操作并理解其目录结构。 创建工作目录以及子目录 `product/` 是第一步[^1]: ```bash cd /tmp mkdir product/ ``` 接着,在该目录下构建目标文件所需的完整路径结构。如果需要测试某个位于 `/usr/share/anaconda/addons` 路径下的插件,则需在此处模拟相同的目录层次结构: ```bash mkdir -p product/usr/share/anaconda/addons ``` 上述命令的作用在于确保所修改的内容能够被正确识别并与实际运行环境匹配。通过这种方式,开发者可以在不破坏原始系统的情况下验证新功能或者修复已知问题。 尽管此部分讨论的是 anaconda installer 中的具体实现细节,但值得注意的是,在其他场景比如地理数据分析领域里也有类似的准备工作推荐——即可以通过 GeoPandas 教程学习 shapefile 的基本概念[^2]。不过这属于另一范畴的应用实例而非直接关联到当前主题上关于 anaconda installation extract files locations and structure 的说明。 对于希望深入研究 Anaconda 安装包内部机制的人士来说,掌握这些基础知识是非常有帮助的;它不仅限于简单的文件复制粘贴动作,更涉及到如何维护一致性和兼容性的高级话题。 ```python import os # Example Python code showing how to programmatically create directories similar to bash commands above. base_path = '/tmp/product' if not os.path.exists(base_path): os.makedirs(base_path) addon_dir = f'{base_path}/usr/share/anaconda/addons' os.makedirs(addon_dir, exist_ok=True) print(f"Directory created at {addon_dir}") ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值