吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 524|回复: 4
收起左侧

[CTF] 【2026春节】解题领红包初级题+Web - 菜的很稳定

[复制链接]
dongye 发表于 2026-3-4 02:57

【2026春节】解题领红包初级题+Web - 菜的很稳定

by dongye

又到了一年春节活动做题环节,感觉今年菜的依旧,只做了初级题和Web,基本依靠AI,对代码敏感度有所下降

解题领红包之二 {Windows 初级题}

程序好像加了IDA的检测,直接调试会进一个系统模块找不到实际逻辑代码,花了点时间做分析

方法1  静态分析
ida打开后搜索现实的字符串 "CrackMe Challenge v2.5 - 2026",可以找到真实的函数入口,跟踪找到 if ( passwordCheck(Str, 31) ) 可知flag长度是31
进入可以看到

bool __cdecl passwordCheck(int a1, int a2)
{
  unsigned __int8 *Block; // ebp
  int v3; // eax
  int v4; // ebx
  bool v5; // dl
  // 申请内存
  Block = (unsigned __int8 *)sub_4AB710(0x64u);
  // 令牌生成
  generateBlock((int)Block);
  // 值比较
  if ( a2 <= 0 )
  {
    v4 = 0;
  }
  else
  {
    v3 = 0;
    v4 = 0;
    do
    {
      v5 = *(char *)(a1 + v3) == Block[v3];
      ++v3;
      v4 += v5;
    }
    while ( a2 != v3 );
  }
  j_j_free(Block);
  // 比较结果返回
  return a2 == v4;
}

代码生成Block,然后与输入值一一比对,也就是说 generateBlock中就是正确flag
进入

_BYTE *__cdecl sub_3E1620(int a1)
{
  _BYTE *result; // eax

  *(_DWORD *)a1 = 758280311;
  *(_DWORD *)(a1 + 4) = 1663511336;
  *(_DWORD *)(a1 + 8) = 1880974179;
  *(_DWORD *)(a1 + 12) = 494170226;
  *(_DWORD *)(a1 + 16) = 842146570;
  *(_DWORD *)(a1 + 20) = 657202491;
  *(_DWORD *)(a1 + 24) = 658185525;
  *(_BYTE *)(a1 + 30) = 99;
  *(_WORD *)(a1 + 28) = 12323;
  result = (_BYTE *)a1;
  do
    *result++ ^= 0x42u;
  while ( result != (_BYTE *)(a1 + 31) );
  *(_BYTE *)(a1 + 31) = 0;
  return result;
}

看不懂,丢给AI,给出python代码

import struct

def generate_block():
    # 1. 整理原始的 DWORD 数据 (32位无符号整数)
    # 这些是从汇编代码中提取出的 4字节 整数
    dwords = [
        758280311,  # a1[0-3]
        1663511336, # a1[4-7]
        1880974179, # a1[8-11]
        494170226,  # a1[12-15]
        842146570,  # a1[16-19]
        657202491,  # a1[20-23]
        658185525   # a1[24-27]
    ]

    # 2. 将 DWORD 转换为字节数组 (使用小端序 'I' 代表 unsigned int)
    content = bytearray()
    for val in dwords:
        content.extend(struct.pack('<I', val))

    # 3. 处理剩余的 3 个字节
    # a1[30] = 99 ('c')
    # *((_WORD *)a1 + 14) = 12323 (0x3023) -> 对应 a1[28] 和 a1[29]
    content.append(0x23) # a1[28]
    content.append(0x30) # a1[29]
    content.append(99)   # a1[30]

    # 4. 执行异或 (XOR 0x42)
    decrypted = ""
    for b in content:
        decrypted += chr(b ^ 0x42)

    return decrypted

if __name__ == "__main__":
    flag = generate_block()
    print(f"解密后的内容为: {flag}")

52pojie!!!_2026_Happy_new_year!

方法2 动态调试
使用OD打开程序,再程序开始搜索字符 "CrackMe Challenge v2.5 - 2026" 可以找到主线程的方法,把 004AD130    8D4C24 04       lea ecx,dword ptr ss:[esp+0x4] 设置为新EIP
再按F8就可以跳过检测,直接进入主程序,然后跟踪比较方法

003E16D4    83EC 1C         sub esp,0x1C
003E16D7    8B7424 34       mov esi,dword ptr ss:[esp+0x34]
003E16DB    8B7C24 30       mov edi,dword ptr ss:[esp+0x30]
003E16DF    C70424 64000000 mov dword ptr ss:[esp],0x64
003E16E6    E8 25A00C00     call 【2026春.004AB710
003E16EB    890424          mov dword ptr ss:[esp],eax
003E16EE    89C5            mov ebp,eax
003E16F0    E8 2BFFFFFF     call 【2026春.003E1620
003E16F5    85F6            test esi,esi
003E16F7    7E 37           jle short 【2026春.003E1730

再F8跟踪就也可以在寄存器中看到正确的flag

eax=011718A7
ebp=01171888, (ASCII "52pojie!!!_2026_Happy_new_year!")

解题领红包之三 {Android 初级题}

额 不知道怎么回事啊 玩了两次就过了。。。。早些年自己写过这种游戏所以玩的比较快
第一次快完成时候又打乱了,本来想看看程序逻辑,打开是混淆过得,用算法助手打点日志看看,然后玩了一次就过了
用jadx打开代码发现混淆过,后续就没有分析的动力了

拼图

拼图

解题领红包之四 {Windows 初级题}

使用IDA打卡还是有反调试,查看代码发现很多python相关的内容,使用Detect查壳 发现是pyinstaller打包的python程序
使用 pyinstxtractor 解包,得到 主程序 crackme_easy.pyc
反编译python时遇到问题,pyc是用过python3.14编译的,uncompyle6和pycdc都不支持
使用 dis 方法输出指令

import marshal
import dis

with open("crackme_easy.pyc","rb") as f:
    f.read(16)  # skip header
    code = marshal.load(f)

dis.dis(code)

得到指令文件,这一步已经属于是源码了,相当于绕过工具对版本的校验,从python中直接拿执行的指令
丢给AI还原成py代码
代码很简单,直接调用generate_flag即可得到 flag
整理后的代码  

import base64
enc_data = 'e3w+fiRvfW18fnx4ZAZ6Pj43YwB9OWMXfXo8Dg4O'
data = base64.b64decode(enc_data)
flag = "".join([chr(b ^ 78) for b in data])
print(flag)

FLAG: 52p0j!3#2026*H4ppy-N3w-Y34r@@@

解题领红包之五 {Windows 中级题}

查壳发现是 Nuitka 打包的Python程序,运行时会释放文件,复制出来或者直接解压exe可以得到文件,里面有个crackme_hard.dll 应该就是实际运行的逻辑
这个dll很大猜测是把真实的程序打包进来的,后面不会解码了,没做出

解题领红包之六 {番外篇 初级题}

Detect 查信息发现是lua程序,附加数据方式为zip,通过AI分析,可以将exe文件解压,解压后发现lua脚本,打开脚本看内容,可直接看到胜利消息方法

local function getWinMessage()
    local content = nil

    if love.filesystem.getInfo("assets/flag.dat") then
        content = love.filesystem.read("assets/flag.dat")
    end

    if not content or currentDifficulty ~= "hard" then
        return "You WIN!"
    end

    local key = "52pojie"
    local keyLen = #key
    local result = {}
    local bit = require("bit")

    for i = 1, #content do
        local b = string.byte(content, i)
        local k = string.byte(key, ((i - 1) % keyLen) + 1)
        table.insert(result, string.char(bit.bxor(b, k)))
    end

    return table.concat(result)
end

如果是easy模式返回 You WIN!,hard模式返回flag

flag是通过 assets/flag.dat 简单异或解密得到的

这份没有lua和love的环境,丢给ai翻译成python

def decrypt_flag():
    key = "52pojie"
    key_len = len(key)

    try:
        # 读取原始加密数据
        with open("flag.dat", "rb") as f:
            content = f.read()

        result = []
        for i in range(len(content)):
            # 获取当前位置对应的密钥字符
            k = ord(key[i % key_len])
            # 执行异或操作
            b = content[i]
            result.append(chr(b ^ k))

        return "".join(result)

    except FileNotFoundError:
        return "错误:未找到 flag.dat 文件"

print("解密结果为:", decrypt_flag())

得到FLAG: flag{52pojie_2026_Happy_NewYear!>w<}

后面又研究了方法2

用010 Editor打开exe文件,十六进制搜索 50 4B 03 04 找到main.lua的资源位置 5E800
导出为zip文件
打开zip文件修改其中的lua代码把hard模式中的3个初始墙改为30,或者简单模式输出FLAG,或者直接输出FLAG,方法就很多了

-- Shuffle and place walls
    local targetWallCount = (currentDifficulty == "easy") and 12 or 3

修改为

-- Shuffle and place walls
    local targetWallCount = (currentDifficulty == "easy") and 12 or 30

用010 Editor打开修改后的zip,复制全部内容,在exe文件中粘贴替换 5E800后续的全部内容即可实现资源的替换
程序没有做任何的校验,直接替换即可
再次打开游戏,选择hard模式满地都是墙随便点几下就可以拿到FLAG

另外补充一下,今年这只猫的逃跑思路会提前躲避点击的位置,这样反而导致很容易堵,即便直接手动堵猫也很容易完成,相较24年的猫就难多了,几乎不可能手动完成

CatchCat

CatchCat

【2026春节】解题领红包之九

验证码生成过程是随机数和uid拼接变换得到验证码,然后经过0x2026轮sha256生成hash校验码
随机数是在js层生成的,为了方便调试把随机数固化,不影响最终的校验,可以不做

imports.wbg.__wbg_getRandomValues_1c61fac11405ffdc = function() { 
            ret = handleError(function (arg0, arg1) {
                        let arr = getArrayU8FromWasm0(arg0, arg1)
                        arr.fill(1);
                        // globalThis.crypto.getRandomValues(arr);
                        console.log("__wbg_getRandomValues_1c61fac11405ffdc arg0: ", arg0, "arg1: ", arg1, "arr: ", arr)
                    }, arguments) 
            return ret
         };

由于在js层和wasm层都做了对验证码的sha256因此在sha256函数的入口下断点应该就能拿到验证码

wasm代码去掉底部的data部分,丢给AI分析可知 func9 是sha256方法,在方法入口下断

(func $func9 (param $var0 i32) (param $var1 i32) (param $var2 i32)
    (local $var3 i32)
    ...
    (local $var35 i32)
    local.get $var0
    // 下断
    i32.load offset=28
    local.set $var33
    local.get $var0
    i32.load offset=24

使用内存查看器,查看参数对应内存地址内容
var1 变量地址,我这里是第二次看到的明文flag
对应的代码在 0x000911e call func9
这时候调试栈回到js,调用取值方法得到flag
1048216是var1参数值,50是验证码长度

getStringFromWasm0(1048216, 50)

得到flag:  flag{fJj7rKZdAeB28zXWWDRLgZgDTBhvWDfcM8QBy5Q3uZfq2TuA9q}

完结


免费评分

参与人数 4威望 +2 吾爱币 +103 热心值 +4 收起 理由
Hmily + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Coxxs + 1 + 1 用心讨论,共获提升!
1588 + 1 + 1 谢谢@Thanks!
局外人K + 1 + 1 我很赞同!

查看全部评分

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

suhaojia 发表于 2026-3-4 03:27
太强了太强了,半夜看到这些简直犯困
zhujf 发表于 2026-3-4 08:24
解题领红包之二我试了2天,都没搞出来,还是我太菜了
前进的小白 发表于 2026-3-4 09:23
fttsh 发表于 2026-3-4 10:45
前进的小白 发表于 2026-3-4 09:23
那个猫不能手动对抗成功吗

AI说理想放置位置下,大约需要 8~13 个初始障碍才可能构造出必胜局面,3个障碍几乎 100% 能逃脱
我反正是玩了半天玩不出来
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2026-3-4 13:07

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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