首页 > 编程语言 >python 逆向

python 逆向

时间:2024-05-16 23:12:22浏览次数:14  
标签:LOAD 逆向 Blowfish CONST python str2 file path

python 逆向 也是 CTF reverse 的一个重要组成部分(废话)。
题目一般会给一个 exe 文件或者 pyc 文件。

工具

工欲善其事,必先利其器,好的工具是必不可少的。

  1. exe 转 pyc 工具:

GitHub - WithSecureLabs/python-exe-unpacker: A helper script for unpacking and decompiling EXEs compiled from python code.
GitHub - pyinstxtractor/pyinstxtractor-ng: PyInstaller Extractor Next Generation

  1. 在线网站(exe->pyc):PyInstaller Extractor WEB
  2. 在线网站(pyc->python):在线Python pyc文件编译与反编译python反编译
  3. 有的时候,在线工具不好用,可以用 pycdc(强推),下载后记得 cmake 构建。
git clone https://github.com/zrax/pycdc.git

基本思路

  1. 如果为如果你明确是一个 python 得到的 exe 文件,用上面的网址,将其转为 .pyc 文件。
  2. 对于.pyc 我个人喜欢有用 pycdc
pycdas.exe xxxx.pyc  	#转字节码
pycdc.exe xxxx.pyc		#转python代码
  1. 如果可以得到完整的 python 代码,那么就简单了,pycdc 转换失败,那么就只好用 pycdas 配合可能解出的代码(可能没有),分析获取源码。
  2. 注意:有的时候要对 Magic Number 进行操作,等我有遇到再补充。

题目

多说无益,上题!

我的 pycadc 初体验

如题目,这个题目是我曾经的 python 逆向入门题。

题目:[NSSRound#22 Basic]ezcrypt

import crypto
from Crypto.Cipher import Blowfish

def encrypt_file(input_path, output_path, key):
Unsupported opcode: BEFORE_WITH
    cipher = Blowfish.new(key, Blowfish.MODE_CBC)
    bs = Blowfish.block_size
# WARNING: Decompyle incomplete
# 反编译有点问题

key = b'crypto.SomeEncode'
input_path = '.\\input'
output_path = '.\\output'
encrypt_file(input_path, output_path, key)

pycdc 得到的结果是有问题的,上 pycdas

NSSCTF.pyc (Python 3.12)
[Code]
    File Name: NSSCTF.py
    Object Name: <module>
    Qualified Name: <module>
    Arg Count: 0
    Pos Only Arg Count: 0
    KW Only Arg Count: 0
    Stack Size: 5
    Flags: 0x00000000
    [Names]
        'crypto'
        'Crypto.Cipher'
        'Blowfish'
        'encrypt_file'
        'key'
        'input_path'
        'output_path'
    [Locals+Names]
    [Constants]
        0
        None
        (
            'Blowfish'
        )
        [Code]
            File Name: NSSCTF.py
            Object Name: encrypt_file
            Qualified Name: encrypt_file
            Arg Count: 3
            Pos Only Arg Count: 0
            KW Only Arg Count: 0
            Stack Size: 6
            Flags: 0x00000003 (CO_OPTIMIZED | CO_NEWLOCALS)
            [Names]
                'Blowfish'
                'new'
                'MODE_CBC'
                'block_size'
                'open'
                'read'
                'len'
                'iv'
                'encrypt'
                'write'
            [Locals+Names]
                'input_path'
                'output_path'
                'key'
                'cipher'
                'bs'
                'file'
                'plaintext'
                'padding_length'
                'encrypted_data'
            [Constants]
                None
                'input'
                'rb'
                b'\x00'
                'wb'
            [Disassembly]
                0       RESUME                          0
                2       LOAD_GLOBAL                     1: NULL + Blowfish
                12      LOAD_ATTR                       2: new
                32      LOAD_FAST                       2: key
                34      LOAD_GLOBAL                     0: Blowfish
                44      LOAD_ATTR                       4: MODE_CBC
                64      CALL                            2
                72      STORE_FAST                      3: cipher
                74      LOAD_GLOBAL                     0: Blowfish
                84      LOAD_ATTR                       6: block_size
                104     STORE_FAST                      4: bs
                106     LOAD_GLOBAL                     9: NULL + open
                116     LOAD_CONST                      1: 'input'
                118     LOAD_CONST                      2: 'rb'
                120     CALL                            2
                128     BEFORE_WITH
                130     STORE_FAST                      5: file
                132     LOAD_FAST                       5: file
                134     LOAD_ATTR                       11: read
                154     CALL                            0
                162     STORE_FAST                      6: plaintext
                164     LOAD_CONST                      0: None
                166     LOAD_CONST                      0: None
                168     LOAD_CONST                      0: None
                170     CALL                            2
                178     POP_TOP
                180     LOAD_FAST                       4: bs
                182     LOAD_GLOBAL                     13: NULL + len
                192     LOAD_FAST_CHECK                 6: plaintext
                194     CALL                            1
                202     LOAD_FAST                       4: bs
                204     BINARY_OP                       6 (%)
                208     BINARY_OP                       10 (-)
                212     STORE_FAST                      7: padding_length
                214     LOAD_FAST                       6: plaintext
                216     LOAD_CONST                      3: b'\x00'
                218     LOAD_FAST                       7: padding_length
                220     BINARY_OP                       5 (*)
                224     BINARY_OP                       13 (+=)
                228     STORE_FAST                      6: plaintext
                230     LOAD_FAST                       3: cipher
                232     LOAD_ATTR                       14: iv
                252     LOAD_FAST                       3: cipher
                254     LOAD_ATTR                       17: encrypt
                274     LOAD_FAST                       6: plaintext
                276     CALL                            1
                284     BINARY_OP                       0 (+)
                288     STORE_FAST                      8: encrypted_data
                290     LOAD_GLOBAL                     9: NULL + open
                300     LOAD_FAST                       1: output_path
                302     LOAD_CONST                      4: 'wb'
                304     CALL                            2
                312     BEFORE_WITH
                314     STORE_FAST                      5: file
                316     LOAD_FAST                       5: file
                318     LOAD_ATTR                       19: write
                338     LOAD_FAST                       8: encrypted_data
                340     CALL                            1
                348     POP_TOP
                350     LOAD_CONST                      0: None
                352     LOAD_CONST                      0: None
                354     LOAD_CONST                      0: None
                356     CALL                            2
                364     POP_TOP
                366     RETURN_CONST                    0: None
                368     PUSH_EXC_INFO
                370     WITH_EXCEPT_START
                372     POP_JUMP_IF_TRUE                1 (to 376)
                374     RERAISE                         2
                376     POP_TOP
                378     POP_EXCEPT
                380     POP_TOP
                382     POP_TOP
                384     JUMP_BACKWARD                   103 (to 180)
                386     COPY                            3
                388     POP_EXCEPT
                390     RERAISE                         1
                392     PUSH_EXC_INFO
                394     WITH_EXCEPT_START
                396     POP_JUMP_IF_TRUE                1 (to 400)
                398     RERAISE                         2
                400     POP_TOP
                402     POP_EXCEPT
                404     POP_TOP
                406     POP_TOP
                408     RETURN_CONST                    0: None
                410     COPY                            3
                412     POP_EXCEPT
                414     RERAISE                         1
        b'crypto.SomeEncode'
        '.\\input'
        '.\\output'
    [Disassembly]
        0       RESUME                          0
        2       LOAD_CONST                      0: 0
        4       LOAD_CONST                      1: None
        6       IMPORT_NAME                     0: crypto
        8       STORE_NAME                      0: crypto
        10      LOAD_CONST                      0: 0
        12      LOAD_CONST                      2: ('Blowfish',)
        14      IMPORT_NAME                     1: Crypto.Cipher
        16      IMPORT_FROM                     2: Blowfish
        18      STORE_NAME                      2: Blowfish
        20      POP_TOP
        22      LOAD_CONST                      3: <CODE> encrypt_file
        24      MAKE_FUNCTION                   0
        26      STORE_NAME                      3: encrypt_file
        28      LOAD_CONST                      4: b'crypto.SomeEncode'
        30      STORE_NAME                      4: key
        32      LOAD_CONST                      5: '.\\input'
        34      STORE_NAME                      5: input_path
        36      LOAD_CONST                      6: '.\\output'
        38      STORE_NAME                      6: output_path
        40      PUSH_NULL
        42      LOAD_NAME                       3: encrypt_file
        44      LOAD_NAME                       5: input_path
        46      LOAD_NAME                       6: output_path
        48      LOAD_NAME                       4: key
        50      CALL                            3
        58      POP_TOP
        60      RETURN_CONST                    1: None

根据上面的逻辑可以复原函数【注意:此代码段中存在一些反汇编特有的标记(如 NULL +),在实际代码中并不存在】

def encrypt_file(input_path, output_path, key):
    # 创建了Blowfish算法的加密对象cipher,使用了给定的密钥key和加密模式CBC(Cipher Block Chaining)。
    cipher = Blowfish.new(key, Blowfish.MODE_CBC)
    bs = Blowfish.block_size # 这行代码获取了Blowfish算法的块大小
    with open(input_path, 'rb') as file:
        plaintext = file.read()

    # 计算了需要添加的填充长度,以确保加密数据的长度是块大小的整数倍。
    padding_length = bs - len(plaintext) % bs 
    # 向明文末尾添加了填充字符\x00,使得明文的长度符合块大小的整数倍。
    plaintext += b'\x00' * padding_length

    # 获取了Blowfish算法使用的初始化向量(IV)8字节
    iv = cipher.iv
    # 对填充后的明文进行加密,并将加密后的数据和IV合并为一个字节串encrypted_data
    encrypted_data = iv + cipher.encrypt(plaintext)
    
    # 输出的 output 
    with open(output_path, 'wb') as file:
        file.write(encrypted_data)

注意 key 的获取,不是简单的 b'crypto.SomeEncode' 这个应该是出题人给的提示,打开刚刚得到的反编译的文件夹,发现有一个神奇的加密文件 pycdc.exe crypto.cpython.pyc

# D:\HuaweiMoveData\Users\86135\Desktop\re\exe\NSSCTF.exe_extracted>pycdc.exe crypto.cpython.pyc
# Source Generated with Decompyle++
# File: crypto.cpython.pyc (Python 3.10)

def encrypt(v, k):
    v0 = v[0]
    v1 = v[1]
    (key0, key1, key2, key3) = (k[0], k[1], k[2], k[3])
    sum = 0
    delta = 0x9E3779B9L
    for _ in range(32):
        sum = sum + delta & 0xFFFFFFFFL
        v0 = v0 + ((v1 << 3) + key0 ^ v1 + sum ^ (v1 >> 4) + key1 ^ 596) & 0xFFFFFFFFL
        v1 = v1 + ((v0 << 3) + key2 ^ v0 + sum ^ (v0 >> 4) + key3 ^ 2310) & 0xFFFFFFFFL
    return (v0, v1)


def encrypt_all(v, k):
    encrypted = []
    for i in range(0, len(v), 2):
        encrypted.extend(encrypt(v[i:i + 2], k))
    return encrypted

v = [
    1396533857,
    0xCC8AE275L,
    0x89FB8A63L,
    940694833]
k = [
    17,
    34,
    51,
    68]
encrypted = encrypt_all(v, k)
SomeEncode = ''.join((lambda .0: for num in .0:
for i in range(4)[::-1]:
chr(num >> 8 * i & 255))(encrypted))

是一个就是简单的魔改tea,解密【具体逆向常用的 TEA XTEA XXTEA RC4 等先挖一个坑】:

def encrypt(v, k):
    v0 = v[0]
    v1 = v[1]
    (key0, key1, key2, key3) = (k[0], k[1], k[2], k[3])
    sum = 0
    delta = 0x9E3779B9
    for _ in range(32):
        sum = sum + delta & 0xFFFFFFFF
        v0 = v0 + ((v1 << 3) + key0 ^ v1 + sum ^ (v1 >> 4) + key1 ^ 596) & 0xFFFFFFFF
        v1 = v1 + ((v0 << 3) + key2 ^ v0 + sum ^ (v0 >> 4) + key3 ^ 2310) & 0xFFFFFFFF
    return (v0, v1)

def decrypt(v, k):
    v0 = v[0]
    v1 = v[1]
    (key0, key1, key2, key3) = (k[0], k[1], k[2], k[3])
    sum = 0xC6EF3720
    delta = 0x9E3779B9
    for _ in range(32):
        v1 =v1 - ((v0 << 3) + key2 ^ v0 + sum ^ (v0 >> 4) + key3 ^ 2310) & 0xFFFFFFFF
        v0 = v0 - ((v1 << 3) + key0 ^ v1 + sum ^ (v1 >> 4) + key1 ^ 596) & 0xFFFFFFFF
        sum = sum - delta & 0xFFFFFFFF
    return (v0, v1)
    
def encrypt_all(v, k):
    encrypted = []
    for i in range(0, len(v), 2):
        encrypted.extend(encrypt(v[i:i + 2], k))
    return encrypted

def decrypt_all(v, k):
    decrypted = []
    for i in range(0, len(v), 2):
        decrypted.extend(decrypt(v[i:i + 2], k))
    return decrypted

v = [1396533857,
    0xCC8AE275,
    0x89FB8A63,
    940694833]
k = [
    17,
    34,
    51,
    68]
encrypted = decrypt_all(v, k)
SomeEncode = ''
for i in range(4):
    for j in range(4):
        SomeEncode += chr(encrypted[i] >> (8 * (3 - j)) & 0xFF)
print(SomeEncode) # EzNssRevProjects

ok,获得 key ,用 key 解密:

from Crypto.Cipher import Blowfish

def decrypt_file(input_path, output_path, key):
    cipher = Blowfish.new(key, Blowfish.MODE_CBC)
    bs = Blowfish.block_size
    with open(input_path, 'rb') as file:
        encrypted_data = file.read()

    # 从加密数据中获取初始化向量
    iv = encrypted_data[:bs]
    # 使用初始化向量设置解密对象的IV
    cipher = Blowfish.new(key, Blowfish.MODE_CBC, iv)
    # 解密数据,忽略初始化向量
    decrypted_data = cipher.decrypt(encrypted_data[bs:])
    
    # 去除填充部分
    decrypted_data = decrypted_data.rstrip(b'\x00')

    with open(output_path, 'wb') as file:
        file.write(decrypted_data)

key = b'EzNssRevProjects' # 密钥
input_path = '.\\output'
output_path = '.\\decrypted_output'
decrypt_file(input_path, output_path, key)

到这里关于 python 就差不多了,剩下的 IDA 打开,就是简单的分析,解密流程:

#include <iostream>
#include <windows.h>
using namespace std;

void de(unsigned char* a1, int* a2, int a3) {
    int v5; // [rsp+20h] [rbp-4h]

    for ( int i = 0; i < a3; ++i) {
        v5 = i % 4;
        // 整理一下
        switch (v5) {
        case 0: a1[i] ^= a2[4] + a2[1]; break;
        case 1: a1[i] ^= a2[0] - a2[2]; break;
        case 2: a1[i] += a2[1] ^ a2[3]; break;
        case 3: a1[i] -= a2[2] + a2[0]; break;
        }
    }
}

int main()
{
	int v4[8]; // [rsp+10h] [rbp-50h] BYREF

	v4[0] = 35;
	v4[1] = 18;
	v4[2] = 69;
	v4[3] = 86;
	v4[4] = 103;
    // 大小端的问题,整体顺序是从前往后看,每一部分的数据是从(后往前看)
    unsigned char flag[] = { 0x37,0x8d,0xf,0xab,0x2d,0x98,0x37,0xb5,0x48,0xa6,0x30,0xda,0xc,0xed,0x1b,0xb8,0x0,0xe9,0x24,0x98,0x17,0x81,0xed,0xb6,0x26,0x8c,0x21,0xde,0x4,0xde};
    int flag_len = sizeof(flag) / sizeof(flag[0]);
    printf("%d\n", flag_len); //30
	de(flag, v4, flag_len);
    for (int i = 0; i < flag_len; i++) {
        printf("%c", flag[i]);
    } # NSSCTF{M1xtru3_Py7h0n_1N_Rev}
	return 0LL;
} 

我的 pycadc 破碎了

题目:第二届黄河流域网络安全技能挑战赛 ezpyc

下载的文件这里找不到了,可以看看网络上有没有。
直接解析是有问题的, 其他方法以后在探索吧。
这里硬读python字节码:
python字节码

逻辑不难,可以分析得到:

import base64
import sys

def check(ininput):
    str1 = 'RitdR+kuGPFoYpX4NVP{PVOx[VSzXhLnO{L2Xkj3[l[8]'
    str2 = list(str1)
    list1 = list(ininput)
    for q in range(len(str2)):
        if list1[q] != str2[q]:
            print('Wrong!!!')
    print('All input is correct!')
def encode(eenv):
    # base64编码
    env = base64.b64encode(eenv.encode('utf-8')).decode('utf-8')
    # 字符串处理
    str2 = list(env)
    for i in range(len(str2)):
        str2[i] = chr(ord(str2[i]) ^ 2)
    for j in range(11):
        str2[j] = chr(ord(str2[j]) - 6)
    for k in range(11, 22):
        str2[k] = chr(ord(str2[k]) + 1)
    for h in range(22, 33):
        str2[h] = str2[h] 
    for l in range(33, 44):
        str2[l] = chr(ord(str2[l]) ^ 3)
    # 字符串重组
    str3 = ''
    for w in range(len(str2)):
        str3 += str2[w]
    return str3
enc = input('Please input your flag:')
ininput = list(enc)
if len(ininput) != 33:
    print('Wrong length!')
    sys.exit()
env = encode(enc)
check(env)

题解:

import base64
str1 = 'RitdR+kuGPFoYpX4NVP{PVOx[VSzXhLnO{L2Xkj3[l[8]'
str2 = list(str1)

print(str2)
for l in range(33, 44):
    str2[l] = chr(ord(str2[l]) ^ 3)
for h in range(22, 33):
    str2[h] = str2[h]
for k in range(11, 22):
    str2[k] = chr(ord(str2[k]) - 1)
for j in range(11):
    str2[j] = chr(ord(str2[j]) + 6)
for i in range(len(str2)):
    str2[i] = chr(ord(str2[i]) ^ 2)
print(str2)
flag = ''.join(str2)
print(len(flag))
print(base64.b64decode(flag))
# 得到结果:flag{293efe59c11c3a41f3e337b96ff}

神奇的数字

这里的本意是要写 Magic Number,但是本菜鸡还没有遇到,遇到了再补充(大概吧)。

标签:LOAD,逆向,Blowfish,CONST,python,str2,file,path
From: https://www.cnblogs.com/harveyX/p/18196920

相关文章

  • Python基础02
    Python基础02基础语法字面量掌握字面量的含义代码中,被写在代码中的固定的值,称之为字面量常见的字面量类型我们目前了解:整数、浮点数、字符串这三类即可如何基于print语句完成各类字面量的输出print(字面量),如:print(10),输出整数10print(13.14),输出浮点数13.......
  • python的一些常用编码技巧(持续更新)
    语法问题我常用的库函数1copy库importcopycopy.deepcopy()2、list库fromtypingimportList获取迭代对象的第一个值方法一:使用list方法my_dict={'a':1,'b':2,'c':3}first_key=list(my_dict.keys())[0]print(first_key)#输出:'a'方法二:使......
  • 数据结构简介及PYTHON里的数据类型
    1、什么是数据结构?先介绍几个概念。信息是目前在生活和工作中最经常听到的一个词,但要给信息这个概念一个容易理解的确切定义并不容易。人们希望用计算机处理的终极对象就是客观存在的各种信息,因此说计算机是处理信息的工具。数据是信息的载体,是指计算机(程序)能够处理的符号形式......
  • 电子书自由:python生成epub电子书
    前言最近在看《剑来》小说,但是kindle在线看不方便,而且我在网上找到的只有600多章,目前最新已经更新到了1200章,为了确保后面有书可读,同时也为了巩固下python技能,于是我找到一个免费的网站,然后通过python获取书籍内容,最终将获取到的内容,生成epub电子书,核心内容如下:获取电子书的内......
  • python部署至k8s解决方案
    前言最近做了一个全文检索的项目,项目之前的架子是别人搭建的,部署方式是docker-compose,到后期这个同事基本上不参与了,后面发布测试的时候,我们觉得这种方式不适合测试环境和线上发版(当然也可能是我们不熟悉,有点不专业了),于是就在他开发的基础上,做了一些调整:修改Dockerfile:把依赖打......
  • Python基础01
    Python基础01学习视频https://www.bilibili.com/video/BV1qW4y1a7fU软件pycharm通过快捷键快速设置字体大小常用快捷键ctrl+alt+s:打开软件设置ctrl+d:复制当前行代码shift+alt+上\下:将当前行代码上移或下移crtl+shift+f10:运行当前代......
  • 逆向 | 驱动IRP通信模板
    逆向|驱动IRP通信模板踩了很多坑,比如说IoCreateDevice以后要删除,符号链接也要在卸载的时候删除啥的。反正存个模板在这儿下次就能套了:#include<ntddk.h>#include<stdio.h>#include<stdlib.h>//0-2047是保留的,可以用2048-4095#defineOPER1CTL_CODE(FILE_DEVICE_UN......
  • 解决Python执行命令时路径空格引发的困扰
    哈喽,大家好,我是木头左!在编程实践中,常常需要通过Python执行系统命令,这包括调用外部程序、脚本或是其他命令行工具。然而,一个看似简单却常被忽视的细节——文件路径中的空格,可能会导致程序意外崩溃或行为异常。本篇文章将深入探讨当路径中包含空格时,如何在Python中正确执行命令,以......
  • Python查询PostgreSQL数据库
    哈喽,大家好,我是木头左!Python与PostgreSQL的连接需要了解如何在Python中连接到PostgreSQL数据库。这通常涉及到使用一个库,如psycopg2,它是Python中用于PostgreSQL的最流行的适配器。安装psycopg2非常简单,可以通过pip进行安装:pipinstallpsycopg2安装完成后,可以使用以下代码......
  • python 面向对象(进阶篇)
    python面向对象(进阶篇) 上一篇《Python面向对象(初级篇)》文章介绍了面向对象基本知识:面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用类是一个模板,模板中包装了多个“函数”供使用(可以讲多函数中公用的变量封装到对象中)对象,根据模板创建的实例(即......