吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4439|回复: 39
收起左侧

[原创] 破解 [CrackMe] 你敢信!是AI自编!python混淆技术结果demo版 思路

  [复制链接]
神奇的人鱼 发表于 2025-12-18 11:34
1. 看到作者这次又升级了,搞了新的花活,有点意思,原帖地址:【新提醒】你敢信!是AI自编!python混淆技术结果demo版 - 吾爱破解 - 52pojie.cn
image.png
2. 开始破解,第一步还是看样本信息,pyinstall编写的,不说了,直接上工具,可以看我之前的帖子获取工具
【新提醒】破解 [CrackMe] 自己平时用py写了个一体化打包,来检验一下好破解吗 思路 - 吾爱破解 - 52pojie.cn
image.png
3. 解包信息
这里展示部分反编译代码
image.png
image.png
这里比较重要,导入了dll
image.png
4. 这个混淆代码使用了Fernet库作为加密手段,并且加载dll实现代码隐藏
这里写一个简单的解密函数

[Python] 纯文本查看 复制代码
import base64
from cryptography.fernet import Fernet

base64_str = "dlNLRERnYnFCMVdmYXJDMnRtUHl5cXFaWFdKYVIwSkRRbE9qNGV3VWYxZz06Z0FBQUFBQnBPNVJ6MWJVOXFqeHpTa21UTVdQbXAya1hUem8yVy1uRjJ4a0pLRDJJTlR6dkpnV1hZU05Kb0oyZmVrQVZWa1VXMm9iWEFqVDI1clZoVUxKbXk0cDBsamlvSlE9PQ=="

raw_data = base64.b64decode(base64_str)

# 2. 转为字符串(假设是 UTF-8)
combined_str = raw_data.decode('utf-8')

# 3. 按 ':' 分割
parts = combined_str.split(':', 1)

if len(parts) != 2:
    raise ValueError("Invalid format: expected 'key:token'")

fernet_key = parts[0]
fernet_token = parts[1]


try:
    f = Fernet(fernet_key)
    decrypted_bytes = f.decrypt(fernet_token)

    # 还原的 Python 代码块
    decrypted_code = decrypted_bytes.decode('utf-8')

    print("--- 还原的代码块 ---")
    print(decrypted_code)
    print("--------------------")

except Exception as e:
    print(f"解密失败,可能密钥不匹配或数据被篡改: {e}")


简单去除一部分混淆后知道了真实的dll还有使用的函数以及加载的base64编码的信息
image.png
5. 编写C语言代码调用这个dll方便我们进行调试,并且把base64信息提取出来放到文件中读取
先看看这个dll信息,加了upx壳
image.png
一键去壳
image.png
看一下导出表和导入表
image.png
image.png
编写C++代码
[C++] 纯文本查看 复制代码
#include <windows.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>

// 函数指针类型
typedef int(__stdcall* RunProtectedScriptFunc)(const char* payload);

// 读取同级目录下的 "test" 文件
std::string readTestFile() {
    std::ifstream file("test", std::ios::binary);
    if (!file) {
        std::cerr << "[!] Failed to open 'test' file in current directory." << std::endl;
        return "";
    }
    std::ostringstream ss;
    ss << file.rdbuf();
    return ss.str();
}

// 清理 Base64 中的空白字符
std::string cleanBase64(const std::string& input) {
    std::string output;
    for (char c : input) {
        if (c != ' ' && c != '\t' && c != '\r' && c != '\n') {
            output += c;
        }
    }
    return output;
}

int main() {
    const char* dllName = "libcrypto-03.dll";
    const char* funcName = "run_protected_script";

    // 1. 加载 DLL
    HMODULE hDll = LoadLibraryA(dllName);
    if (!hDll) {
        std::cerr << "[!] Failed to load DLL: " << dllName << std::endl;
        return 1;
    }

    // 2. 获取函数地址
    RunProtectedScriptFunc runScript = (RunProtectedScriptFunc)GetProcAddress(hDll, funcName);
    if (!runScript) {
        std::cerr << "[!] Failed to get function: " << funcName << std::endl;
        FreeLibrary(hDll);
        return 1;
    }

    // 3. 读取 "test" 文件
    std::string base64Raw = readTestFile();
    if (base64Raw.empty()) {
        FreeLibrary(hDll);
        return 1;
    }

    // 4. 清理 Base64
    std::string base64Clean = cleanBase64(base64Raw);
    std::cout << "[+] Loaded 'test' file, length: " << base64Clean.length() << " chars" << std::endl;

    // 5. 调用 DLL
    std::cout << "[*] Calling run_protected_script..." << std::endl;
    int result = runScript(base64Clean.c_str());
    std::cout << "[+] Function returned: " << result << std::endl;

    // 6. 清理
    FreeLibrary(hDll);
    return 0;
}

把编译后的exe文件放入dll目录中,注意还需要补一下python环境
创建lib目录,把base_library.zip内容和cryptography以及cryptography-46.0.3.dist-info都复制进去
image.png
开始动调,细节就不说了,在内存中可以找到还原的代码,把这个代码dump出来
image.png
6. 处理代码
这个代码很长4000多行,采用了很多手段防止静态分析,添加了很多垃圾代码,还有很多平坦流等等
image.png
编写ast解析代码并去除混淆,也是废了很多功夫把混淆去除了一大部分这里给出部分代码,虚拟机类的run函数还没有完全去除混淆就不贴了
大致逻辑如下:是一个虚拟机,最终对比的hash是否一致
[Python] 纯文本查看 复制代码
import sys
import base64
from datetime import datetime

# 加密的 base64 数据
ENCRYPTED_B64_DATA = 'AkFPZ26w1cinXt2On+slFVU5GVN9WWtmeVkzMzI2IANXQIq+b19qaxJfMjM0MW4GVkBPYWxca2Z8XDY1MyQwAFRAXnZvX28qeF8yMDI2IwFVQU9nbF1vcdq02vV8s/rKyJTVsvP0roiGDTZUJZbK5oEPxIO9zorErOii9M7HtOW7zbiYe+6DkhzY+/e/4//U7OOfzKPEpaEdTJHb0/VvhqK307HQic7msZvyeFc='

# XOR 解密密钥(用于解密主 payload)
MAIN_XOR_KEY = bytes([84, 65, 79, 103, 111, 95, 107, 101, 121, 95, 50, 48, 50, 53, 33, 0])

def xor_decrypt(data: bytes, key: bytes) -> bytes:
    """对数据进行 XOR 解密"""
    return bytes(b ^ key[i % len(key)] for i, b in enumerate(data))

class StringTableParser:
    """
    解析加密数据中的字符串表。
    数据格式:
      - 前 4 字节:字符串表的偏移(小端)
      - 从该偏移开始:
          1 字节:字符串数量 N
          接下来 N 个字符串,每个以 1 字节长度 L 开头,后跟 L 字节加密数据
    每个字符串用固定密钥 'StrEncKey_2025!' XOR 解密
    """

    def __init__(self, encrypted_payload: bytes):
        self.payload = encrypted_payload
        self.strings = []

        if len(encrypted_payload) < 4:
            return

        # 读取字符串表偏移(小端序)
        string_table_offset = int.from_bytes(encrypted_payload[:4], 'little')
        if string_table_offset >= len(encrypted_payload):
            return

        offset = string_table_offset
        string_count = encrypted_payload[offset]
        offset += 1

        # 解密每个字符串
        for _ in range(string_count):
            if offset >= len(encrypted_payload):
                break

            length = encrypted_payload[offset]
            offset += 1

            if offset + length > len(encrypted_payload):
                self.strings.append('')  # 数据损坏,添加空串
                offset += 1
                continue

            encrypted_str = encrypted_payload[offset:offset + length]
            offset += length

            # 使用固定密钥解密字符串
            str_key = b'StrEncKey_2025!'
            decrypted = bytes(
                encrypted_str[i] ^ str_key[i % len(str_key)]
                for i in range(len(encrypted_str))
            )

            try:
                decoded_str = decrypted.decode('utf-8')
            except UnicodeDecodeError:
                decoded_str = decrypted.decode('utf-8', errors='replace')

            self.strings.append(decoded_str)

    def run(self):
        """当前为空,无实际执行逻辑"""
        pass

# 主流程
if __name__ == '__main__':
    # 1. Base64 解码
    decoded_data = base64.b64decode(ENCRYPTED_B64_DATA)

    # 2. 用 MAIN_XOR_KEY 解密主 payload
    decrypted_payload = xor_decrypt(decoded_data, MAIN_XOR_KEY)

    # 3. 解析字符串表
    parser = StringTableParser(decrypted_payload)
    parser.run() 


7. hash函数
找到最终的hash函数实现方式

一个自定义的非加密哈希函数,其设计融合了多种常见哈希构造技巧,但是输出空间小(32位)
这就给我们可以进行hash碰撞的机会
[Python] 纯文本查看 复制代码
def custom_hash_simple(data: bytes) -> int:
    hash_value = 2596069104
    for i, b in enumerate(data):
        hash_value = (hash_value * 2654435761 + b + (i ^ 21930)) & 0xFFFFFFFF
        hash_value = (hash_value ^ (hash_value >> 11)) & 0xFFFFFFFF
        hash_value = ((hash_value << 7) | (hash_value >> 25)) & 0xFFFFFFFF

    for const in [305419896, 2596069104, 4275878552]:
        hash_value = ((hash_value ^ const) * 16777619) & 0xFFFFFFFF
    return hash_value


8. 进行hash碰撞
代码中的这部分是最终的判断逻辑,如果我们输入555,计算的hash结果是1131975512,而代码中的期望值是3653593870
image.png
编写C++继续hash碰撞
[C++] 纯文本查看 复制代码
#include <iostream>
#include <vector>
#include <string>
#include <cstdint>
#include <atomic>
#include <thread>
#include <cstring>
#include <mutex>

const uint32_t MOD_INVERSE = 899433627U;
const int TARGET_FIND_COUNT = 5; // 目标找到 5 个

std::atomic<int> found_count(0); // 全局计数器
std::mutex cout_mutex;           // 用于保护标准输出的锁

// === 哈希函数保持不变 ===
inline uint32_t fast_hash_core(const char* data, size_t len) {
    uint32_t hash_value = 2596069104U;
    for (size_t i = 0; i < len; ++i) {
        hash_value = (hash_value * 2654435761U + (uint8_t)data[i] + (static_cast<uint32_t>(i) ^ 21930U)) & 0xFFFFFFFFU;
        hash_value = (hash_value ^ (hash_value >> 11)) & 0xFFFFFFFFU;
        hash_value = ((hash_value << 7) | (hash_value >> 25)) & 0xFFFFFFFFU;
    }
    return hash_value;
}

uint32_t pre_process_target(uint32_t target) {
    uint32_t v = target;
    const uint32_t constants[] = { 4275878552U, 2596069104U, 305419896U };
    for (uint32_t const_val : constants) {
        v = (v * MOD_INVERSE) & 0xFFFFFFFFU;
        v ^= const_val;
    }
    return v;
}

// === 搜索逻辑修改 ===
const std::string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

void search_worker(uint32_t processed_target, std::string prefix, int max_len) {
    char buffer[16];
    int prefix_len = prefix.length();
    std::memcpy(buffer, prefix.c_str(), prefix_len);

    // 使用递归 DFS 搜索
    auto solve = [&](auto self, int depth) -> void {
        // 如果已经找到足够数量,立即退出递归
        if (found_count.load() >= TARGET_FIND_COUNT || depth >= max_len) return;

        for (char c : charset) {
            if (found_count.load() >= TARGET_FIND_COUNT) return;

            buffer[depth] = c;
            uint32_t h = fast_hash_core(buffer, depth + 1);

            if (h == processed_target) {
                // 加锁保证输出完整性并安全增加计数器
                std::lock_guard<std::mutex> lock(cout_mutex);
                if (found_count.load() < TARGET_FIND_COUNT) {
                    found_count++;
                    std::cout << "[!] 找到碰撞 #" << found_count << ": "
                        << std::string(buffer, depth + 1) << std::endl;
                }
                if (found_count.load() >= TARGET_FIND_COUNT) return;
            }

            // 继续向下递归
            self(self, depth + 1);
        }
        };

    solve(solve, prefix_len);
}

int main() {
    // 目标哈希值(示例)
    uint32_t original_target = 3653593870U;
    uint32_t processed_target = pre_process_target(original_target);

    std::cout << "--- 开始破解 ---" << std::endl;
    std::cout << "目标结果数: " << TARGET_FIND_COUNT << std::endl;
    std::cout << "正在启动多线程扫描...\n" << std::endl;

    int max_length = 6;
    std::vector<std::thread> threads;

    // 为字符集中的每个首字母开一个线程(或者根据 CPU 核心数分组)
    // 这里我们启动更多的线程以充分利用多核
    for (size_t i = 0; i < charset.size(); ++i) {
        threads.emplace_back(search_worker, processed_target, std::string(1, charset[i]), max_length);
    }

    for (auto& t : threads) {
        if (t.joinable()) t.join();
    }

    if (found_count.load() >= TARGET_FIND_COUNT) {
        std::cout << "\n任务完成:已找到 " << TARGET_FIND_COUNT << " 个结果。" << std::endl;
    }
    else {
        std::cout << "\n扫描结束,共找到 " << found_count.load() << " 个结果。" << std::endl;
    }

    return 0;
}

很快啊找到了合适的结果

image.png
验证两个,正确
image.png
image.png


免费评分

参与人数 12吾爱币 +12 热心值 +10 收起 理由
ytfh1131 + 1 + 1 我很赞同!
luliucheng + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
crisili + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
R8848 + 1 我很赞同!
lsb2pojie + 1 + 1 热心回复!
zweni + 1 + 1 热心回复!
Die0 + 1 + 1 热心回复!
a4943015 + 1 + 1 大佬能不能帮我破解一个软件
helian147 + 1 + 1 热心回复!
1346 + 1 + 1 谢谢@Thanks!
我的爱是你 + 1 + 1 用心讨论,共获提升!
纳兰龙珠 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

ww7996 发表于 2025-12-23 17:42
大佬 我请教一下,我经常遇到python的程序,无论是pyinstaller打包的 或者是 NUK的打包得 有的他是抓不了包,问AI说是有什么SSL验证,HTTPS得这种,能不能有个外部工具 或者写个脚本 禁用掉这个这个SLL验证把他关闭,或者打补丁什么的,能解包的话还好改,是想在不动源程序, 通过外部来修改 求思路
 楼主| 神奇的人鱼 发表于 2025-12-22 09:56
Hades-52pojie 发表于 2025-12-22 09:50
他这个Python不能直接运行,是每次分发都要自带一个作者的解释器吗.那这样不如用其它语言了....

这个可以直接运行,作者搞了一个自己的加密混淆器,把原始的代码给添加了一些平坦化用于迷惑分析人员,内部包装了一个虚拟机
170077000 发表于 2025-12-18 12:12
wolaileo 发表于 2025-12-18 12:17
真厉害。
wapj3076 发表于 2025-12-18 12:27
太深奥了,看不懂!但顶楼主!
HQF0729 发表于 2025-12-18 12:36
看不明白,但觉得厉害,不明觉厉,所以顶
MySinic 发表于 2025-12-18 13:02
想不到AI自编的都这么厉害了?
xieweixutao 发表于 2025-12-18 13:49
根本就看不懂,楼主好厉害啊。
grin1 发表于 2025-12-18 14:09
厉害了 楼主。
qwe8511066 发表于 2025-12-18 15:07
厉害  厉害  
根本就看不懂
coolcalf 发表于 2025-12-18 15:34
有一天,把文件丢给AI.....
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-12-25 16:18

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表