NepCTF 2025

Solve

Realme

initterm与initterm_e都会执行起始指针First到结束指针Last中的方法(全局构造函数),后者会 捕获并处理 在构造函数中抛出的异常,前者若发生异常会直接崩溃
image|720x350
找到关键的smc函数,对着解密即可
image-2
image-3

Crackme

一道QT逆向,对于图形化逆向,主要找按钮发出的信号对应的槽函数,这题直接根据字符串定位sub_4021C0()
^[0-9a-fA-F]{2,}$是检测输入位16进制数,后面还有检测长度位16个十六进制数
image-4
qword_409040()函数对应sign()用来创建密钥与密文,用c加载libcrypto.dll,调用sign生成密钥与密文
image-5|720x350
aes_encrypt用了flat混淆,调试看有无魔改:AES加密在mixcolumn位置,将state异或了0x55

#include "aes.h"  
#include <stdio.h>  
#include <stdint.h>  
#include <windows.h>  
#include <iostream>  
#include <vector>  
  
#define AES128 1  
  
static void phex(uint8_t *str) {  
  
#if defined(AES256)  
    uint8_t len = 32;  
#elif defined(AES192)  
    uint8_t len = 24;  
#elif defined(AES128)  
    uint8_t len = 16;  
#endif  
    unsigned char i;  
    for (i = 0; i < len; ++i)  
        printf("%.2x", str[i]);  
    printf("\n");  
}  
  
typedef int (*Func)(uint8_t *, int, uint8_t *);  // 声明函数指针类型  
  
int main() {  
    struct AES_ctx ctx;  
  
    std::string name[] = {  
            "qinzhang",  
            "mq",  
            "hd",  
            "chenmin",  
            "user",  
            "cjh",  
            "cyy",  
            "yw",  
            "liujun",  
            "ob",  
            "chenmei",  
            "ok",  
            "ltt",  
            "fgao",  
            "minzhao",  
            "haoliu",  
            "dp",  
            "zhangfang",  
            "kefuadmin",  
            "chenchao",  
            "jq",  
            "haozhang",  
            "zhangyan",  
            "xiulanyang",  
            "eo",  
            "liudan",  
            "minhuang",  
            "zhoujing",  
            "liyz",  
            "jzhang",  
            "sxy",  
            "liujie",  
            "wjj",  
            "zhoujie",  
            "wangk",  
            "lixiuzhen",  
            "zhangyl",  
            "lxy",  
            "hongyang",  
            "nu",  
            "liuyy",  
            "liangchen",  
            "lgr",  
            "cb",  
            "wangxiulan",  
            "kc",  
            "od",  
            "gzzhang",  
            "vh",  
            "chenf",  
            "taoliu",  
            "xb",  
            "wanggz",  
            "hmli",  
            "mm",  
            "xzzhang",  
            "wangbo",  
            "jhchen",  
            "cmli",  
            "danzhang",  
            "ligy",  
            "wangfy",  
            "jianhuawang",  
            "gn",  
            "wangx",  
            "wxm",  
            "liufang",  
            "km",  
            "hyang",  
            "xe",  
            "liwei",  
            "sp",  
            "xiuyingwu",  
            "danliu",  
            "yongzhou",  
            "hv",  
            "xinchen",  
            "test",  
            "wangsy",  
            "lb",  
            "xiuzhenyang",  
            "guilanli",  
            "liyun",  
            "yangxz",  
            "iq",  
            "huanliu",  
            "yuyingchen",  
            "zhanggl",  
            "meizhang",  
            "lsz",  
            "pingwang",  
            "wzq",  
            "ot",  
            "gh",  
            "lingli",  
            "tu",  
            "yumeiwang",  
            "zhangxh",  
            "ro",  
            "hchen"  
    };  
  
    HMODULE hDll = LoadLibraryA("libcrypto.dll");  
    if (!hDll) {  
        std::cerr << "LoadLibrary failed" << std::endl;  
        return 1;  
    }  
    // 获取函数地址  
    Func sign = (Func) GetProcAddress(hDll, "sign");  
    for (const auto &item: name) {  
        const auto &sss = item + "Showmaker11";  
        int len1 = sss.size();  
        std::vector<uint8_t> buffer{};  
        std::vector<uint8_t> key{};  
  
        buffer.resize(16);  
        key.resize(16);  
        int len2 = item.size();  
        sign((uint8_t *) sss.data(), len1, buffer.data());  
        sign((uint8_t *) item.data(), len2, key.data());  
        AES_init_ctx(&ctx, key.data());  
        AES_ECB_decrypt(&ctx, buffer.data());  
        phex(buffer.data());  
        std::cout<<std::endl;  
    }  
    printf("\n");  
}

QRS

XSafe

一堆混淆与花指令,过了一部分,有些bug之后再修

import idapro  
import idc  
import ida_bytes  
  
  
def get_number(ea, n):  
    print(idc.get_operand_value(ea, n))  
  
  
def nop(start_addr, end_addr):  
    print(hex(start_addr), hex(end_addr))  
    for i in range(start_addr, end_addr):  
        ida_bytes.patch_byte(i, 0x90)  
    start_addr = end_addr  
    start_addr -= idc.get_item_size(start_addr)  
  
  
def calladd5(start_addr, end_addr):  
    reg_map = {  
        'rax': 0,  
        'rcx': 1,  
        'rdx': 2,  
        'rsi': 6,  
        'rdi': 7,  
        'r8': 8,  
        'r10': 10,  
        'r11': 11,  
        'r14': 14  
    }  
    reg = {  
        0: 0,  # rax  
        1: 0,  # rcx  
        2: 0,  
        6: 0,  # rsi  
        7: 0,  # rdi  
        8: 0,  
        10: 0,  
        11: 0,  
        14: 0  # r14  
    }  
  
    ea = start_addr  
    while ea != end_addr:  
        idc.del_items(ea, flags=False)  
        idc.create_insn(ea)  
  
        if idc.print_insn_mnem(ea) == 'call':  
            is_end = idc.get_operand_value(ea, 0)  
            idc.create_insn(is_end)  
            if idc.get_bytes(ea + 1, 4) == b'\x00\x00\x00\x00' and idc.print_insn_mnem(is_end) == 'add':  
                is_end = ea + 5 + idc.get_operand_value(ea + 5, 1)  
                idc.create_insn(is_end)  
                if idc.print_insn_mnem(is_end) == 'pop':  
                    if idc.get_operand_value(is_end, 1) == 0 or idc.get_operand_value(is_end, 1) == 1:  
                        is_end += 1  
                    else:  
                        is_end += 2  
                    nop(ea, is_end)  
                    ea += idc.get_item_size(ea)  
                    continue  
            if idc.print_insn_mnem(is_end) == 'add' and idc.get_operand_value(is_end, 0) == 4:  
                idc.create_insn(is_end + 5)  
                if idc.print_insn_mnem(is_end + 5) == 'retn':  
                    is_end = ea + 5 + idc.get_operand_value(is_end, 1)  
                    nop(ea, is_end)  
  
        elif idc.print_insn_mnem(ea) == 'jmp':  
            if idc.get_operand_type(ea, 0) == 1:  
                target = reg[idc.get_operand_value(ea, 0)]  
                offset = target - ea  
                if offset < 0:  
                    offset = 0x100 + offset  
                else:  
                    offset -= 2  
                if offset > 0x100:  
                    ida_bytes.patch_bytes(ea - 3, b'\xE9' + offset.to_bytes(4, 'little', signed=True))  
                else:  
                    ida_bytes.patch_bytes(ea, b'\xEB' + offset.to_bytes())  
                    ea = target - idc.get_item_size(ea)  # 结尾+1  
                print(hex(target))  
            else:  
                is_end = idc.get_operand_value(ea, 0)  
                idc.create_insn(is_end)  
                if idc.print_insn_mnem(is_end) == 'call' and ida_bytes.get_bytes(idc.get_operand_value(is_end, 0),  
                                                                                 1) == b'\xc3':  
                    nop(ea, is_end + 5)  
                else:  
                    ea = is_end  
  
        elif idc.print_insn_mnem(ea) == 'push':  
            idc.create_insn(ea + 1)  
            if idc.print_insn_mnem(ea + 1) == 'call':  
                is_end = idc.get_operand_value(ea + 1, 0)  
                idc.create_insn(is_end)  
                if idc.print_insn_mnem(is_end) == 'pop' and idc.print_insn_mnem(is_end + 1) == 'pop':  
                    is_end += 2  
                    nop(ea, is_end)  
            idc.create_insn(ea + 2)  
            if idc.print_insn_mnem(ea + 2) == 'call':  
                is_end = idc.get_operand_value(ea + 2, 0)  
                idc.create_insn(is_end)  
                if idc.print_insn_mnem(is_end) == 'pop' and idc.print_insn_mnem(is_end + 2) == 'pop':  
                    is_end += 4  
                    nop(ea, is_end)  
  
        elif (idc.print_insn_mnem(ea) == 'jz' and idc.print_insn_mnem(ea + 2) == 'jnz' or  
              idc.print_insn_mnem(ea) == 'jnz' and idc.print_insn_mnem(  
                    ea + 2) == 'jz') and idc.get_operand_value(ea, 0) == idc.get_operand_value(ea + 2, 0):  
            is_end = idc.get_operand_value(ea, 0)  
            nop(ea, is_end)  
  
        elif idc.print_insn_mnem(ea) == 'or':  
            is_end = ea + idc.get_item_size(ea)  
            idc.create_insn(is_end)  
            if idc.print_insn_mnem(is_end) == 'jnz' or idc.print_insn_mnem(is_end) == 'jmp':  
                is_end = idc.get_operand_value(is_end, 0)  
            elif idc.print_insn_mnem(is_end) == 'shr':  
                idc.create_insn(is_end + 4)  
                if idc.print_insn_mnem(is_end) == 'and':  
                    is_end = is_end + idc.get_item_size(is_end)  
                    while True:  
                        idc.create_insn(is_end)  
                        if idc.print_insn_mnem(is_end) == 'jnz' or idc.print_insn_mnem(is_end) == 'jmp':  
                            is_end = idc.get_operand_value(is_end, 0)  
                            nop(ea, is_end)  
                            break  
                        is_end += idc.get_item_size(is_end)  
            nop(ea, is_end)  # 连mov一起nop需要ea-xxxxxx  
  
        elif idc.print_insn_mnem(ea) == 'sub' and idc.get_operand_value(ea, 0) == idc.get_operand_value(ea, 1):  
            is_end = ea  
            while True:  
                is_end += 1  
                idc.create_insn(is_end)  
                if idc.print_insn_mnem(is_end) == 'jz':  
                    is_end = idc.get_operand_value(is_end, 0)  
                    break  
                elif idc.print_insn_mnem(is_end) == 'jmp':  
                    is_end = idc.get_operand_value(is_end, 0)  
                    break  
            nop(ea, is_end)  # 连mov一起nop需要ea-xxxxxx  
  
        elif idc.print_insn_mnem(ea) == 'movq' and idc.get_operand_value(ea, 0) == 10:  
            is_end = ea  
            while True:  
                is_end += idc.get_item_size(is_end)  
                idc.create_insn(is_end)  
                if idc.print_insn_mnem(is_end) == 'sub':  
                    while True:  
                        is_end += idc.get_item_size(is_end)  
                        idc.create_insn(is_end)  
                        if idc.print_insn_mnem(is_end) == 'jnz' or idc.print_insn_mnem(is_end) == 'jmp':  
                            is_end = idc.get_operand_value(is_end, 0)  
                            break  
                    break            nop(ea, is_end)  
  
        elif idc.print_insn_mnem(ea) == 'add':  
            if idc.get_operand_type(ea, 1) == 1 and idc.get_operand_type(ea, 0) == 1:  
                reg[idc.get_operand_value(ea, 0)] = (reg[idc.get_operand_value(ea, 1)] + reg[  
                    idc.get_operand_value(ea, 0)]) & 0xffffffffffffffff  
                print(hex(ea), ': add ', idc.get_operand_value(ea, 0), idc.get_operand_value(ea, 1), reg)  
            if idc.get_operand_value(ea, 0) == idc.get_operand_value(ea, 1):  
                is_end = ea + idc.get_item_size(ea)  
                while True:  
                    idc.create_insn(is_end)  
                    if idc.print_insn_mnem(is_end) == 'jnz' or idc.print_insn_mnem(is_end) == 'jmp':  
                        is_end = idc.get_operand_value(is_end, 0)  
                        nop(ea, is_end)  
                        break  
                    is_end += idc.get_item_size(is_end)  
  
        elif idc.print_insn_mnem(ea) == 'mov':  
            idc.create_insn(ea + 3)  
            if idc.print_insn_mnem(ea + 3) == 'and' and idc.get_operand_value(ea, 0) == idc.get_operand_value(  
                    ea + 3, 0):  
                is_end = ea + 4  
                while True:  
                    idc.create_insn(is_end)  
                    if idc.print_insn_mnem(is_end) == 'jz':  
                        is_end = idc.get_operand_value(is_end, 0)  
                        break  
                    is_end += 1  
                nop(ea, is_end)  
            elif idc.get_operand_value(ea, 0) in reg:  
                if idc.get_operand_type(ea, 1) == 5:  # 常数赋值  
                    reg[idc.get_operand_value(ea, 0)] = idc.get_operand_value(ea, 1)  
                elif idc.get_operand_type(ea, 1) == 2:  
                    reg[idc.get_operand_value(ea, 0)] = idc.get_qword(idc.get_operand_value(ea, 1))  
                elif idc.get_operand_type(ea, 1) == 3:  
                    if idc.get_operand_value(ea, 1) == 4:  
                        if idc.get_operand_value(ea, 0) == 0:  
                            reg1 = reg_map['rax']  
                            reg2 = reg_map['rcx']  
                        elif idc.get_operand_value(ea, 0) == 2:  
                            reg1 = reg_map['rdx']  
                            reg2 = reg_map['rcx']  
  
                    elif idc.get_operand_value(ea, 1) == 12:  
                        if idc.get_operand_value(ea, 0) == 8:  
                            reg1 = reg_map['r8']  
                            reg2 = reg_map['r10']  
                        elif idc.get_operand_value(ea, 0) == 11:  
                            reg1 = reg_map['r10']  
                            reg2 = reg_map['r11']  
  
                    reg[idc.get_operand_value(ea, 0)] = idc.get_qword((reg[reg1] + reg[reg2]) & 0xffffffffffffffff)  
                print(hex(ea), ': mov ', idc.get_operand_value(ea, 0), idc.get_operand_value(ea, 1), reg)  
  
        ea += idc.get_item_size(ea)  
  
  
if __name__ == '__main__':  
    file_name = r"D:\ctf\ctf_game\2025\7\NepNep\XSafe.sys.i64"  
    print(f"Opening database {file_name}...")  
    # Open database, analyze it and close it at last  
    idapro.open_database(file_name, True)  
    calladd5(0x140015e5e, 0x140144DCF)  
    idapro.close_database(False)

What I have learned

  1. 查看initterm与initterm_e有无可疑函数时,一定要仔细看,每个函数偏移都看一看
  2. aes加密在调试时,会因为列优先排序,行优先排序导致每一次的state的结果不同,但最终结果相同,且不同的结果也仅仅是行变化与列变化的区别,用tiny-aes调试魔改时可以肉眼转化一下,如果发现对不上,再细看此步魔改了什么
  3. 如何主动调用dll中的函数
  4. QT逆向,见learn learn QT Reverse
posted @ 2025-07-28 17:51  Un1corn  阅读(238)  评论(0)    收藏  举报