LitCTF2025 reverse wp

pickle

用010打开看到有python相关的,猜测就是python逆向

像这种感觉可能都能够用dis.dis解决

import dill
import dis

with open("challenge.pickle", "rb") as f:
    func = dill.load(f)

# 反编译为字节码
dis.dis(func)

输出为python字节码

  5           0 RESUME                   0

  6           2 LOAD_GLOBAL              1 (NULL + input)
             12 CACHE
             14 LOAD_CONST               1 ('input your flag > ')
             16 UNPACK_SEQUENCE          1
             20 CALL                     1
             28 CACHE
             30 STORE_SUBSCR
             34 CACHE
             36 CACHE
             38 CACHE
             40 CACHE
             42 CACHE
             44 CACHE
             46 CACHE
             48 CACHE
             50 CACHE
             52 UNPACK_SEQUENCE          0
             56 CALL                     0
             64 CACHE
             66 STORE_FAST               0 (user_input)

  8          68 BUILD_LIST               0
             70 STORE_FAST               1 (decrypted)

  9          72 LOAD_GLOBAL              5 (NULL + range)
             82 CACHE
             84 LOAD_GLOBAL              7 (NULL + len)
             94 CACHE
             96 LOAD_FAST                0 (user_input)
             98 UNPACK_SEQUENCE          1
            102 CALL                     1
            110 CACHE
            112 UNPACK_SEQUENCE          1
            116 CALL                     1
            124 CACHE
            126 GET_ITER
        >>  128 FOR_ITER                34 (to 200)

 10         132 LOAD_FAST                0 (user_input)
            134 LOAD_FAST                2 (i)
            136 BINARY_SUBSCR
            140 CACHE
            142 CACHE
            144 CACHE
            146 LOAD_CONST               2 (6)
            148 BINARY_OP               10 (-)
            152 STORE_FAST               3 (b)

 11         154 LOAD_FAST                1 (decrypted)
            156 STORE_SUBSCR
            160 CACHE
            162 CACHE
            164 CACHE
            166 CACHE
            168 CACHE
            170 CACHE
            172 CACHE
            174 CACHE
            176 CACHE
            178 LOAD_FAST                3 (b)
            180 UNPACK_SEQUENCE          1
            184 CALL                     1
            192 CACHE
            194 POP_TOP
            196 JUMP_BACKWARD           35 (to 128)

 13         198 BUILD_LIST               0
        >>  200 LOAD_CONST               3 ((85, 84, 174, 227, 132, 190, 207, 142, 77, 24, 235, 236, 231, 213, 138, 153, 60, 29, 241, 241, 237, 208, 144, 222, 115, 16, 242, 239, 231, 165, 157, 224, 56, 104, 242, 128, 250, 211, 150, 225, 63, 29, 242, 169))
            202 LIST_EXTEND              1
            204 STORE_FAST               4 (fflag)

 14         206 BUILD_LIST               0
            208 LOAD_CONST               4 ((19, 55, 192, 222, 202, 254, 186, 190))
            210 LIST_EXTEND              1
            212 STORE_FAST               5 (key_ints)

 16         214 LOAD_CONST               5 (<code object encrypt at 0x00000276188D2BB0, file "d:\code\PYTHON\IPParser1.py", line 16>)
            216 MAKE_FUNCTION            0
            218 STORE_FAST               6 (encrypt)

 23         220 PUSH_NULL
            222 LOAD_FAST                6 (encrypt)
            224 LOAD_FAST                4 (fflag)
            226 LOAD_FAST                5 (key_ints)
            228 UNPACK_SEQUENCE          2
            232 CALL                     2
            240 CACHE
            242 STORE_FAST               7 (encrypted_flag)

 25         244 LOAD_FAST                1 (decrypted)
            246 LOAD_FAST                7 (encrypted_flag)
            248 COMPARE_OP               2 (<)
            252 CACHE
            254 POP_JUMP_IF_FALSE       17 (to 290)

 26         256 LOAD_GLOBAL             11 (NULL + print)
            266 CACHE
            268 LOAD_CONST               6 ('Good job! You made it!')
            270 UNPACK_SEQUENCE          1
            274 CALL                     1
            282 CACHE
            284 POP_TOP
            286 LOAD_CONST               0 (None)
            288 RETURN_VALUE

 28     >>  290 LOAD_GLOBAL             11 (NULL + print)
            300 CACHE
            302 LOAD_CONST               7 ("Nah, don't give up!")
            304 UNPACK_SEQUENCE          1
            308 CALL                     1
            316 CACHE
            318 POP_TOP
            320 LOAD_CONST               0 (None)
            322 RETURN_VALUE

Disassembly of <code object encrypt at 0x00000276188D2BB0, file "d:\code\PYTHON\IPParser1.py", line 16>:
 16           0 RESUME                   0

 17           2 BUILD_LIST               0
              4 STORE_FAST               2 (result)

 18           6 LOAD_GLOBAL              1 (NULL + range)
             16 CACHE
             18 LOAD_GLOBAL              3 (NULL + len)
             28 CACHE
             30 LOAD_FAST                0 (flag_bytes)
             32 UNPACK_SEQUENCE          1
             36 CALL                     1
             44 CACHE
             46 UNPACK_SEQUENCE          1
             50 CALL                     1
             58 CACHE
             60 GET_ITER
        >>   62 FOR_ITER                56 (to 178)

 19          66 LOAD_FAST                0 (flag_bytes)
             68 LOAD_FAST                3 (i)
             70 BINARY_SUBSCR
             74 CACHE
             76 CACHE
             78 CACHE
             80 LOAD_FAST                1 (key)
             82 LOAD_FAST                3 (i)
             84 LOAD_GLOBAL              3 (NULL + len)
             94 CACHE
             96 LOAD_FAST                1 (key)
             98 UNPACK_SEQUENCE          1
            102 CALL                     1
            110 CACHE
            112 BINARY_OP                6 (%)
            116 BINARY_SUBSCR
            120 CACHE
            122 CACHE
            124 CACHE
            126 BINARY_OP               12 (^)
            130 STORE_FAST               4 (b)

 20         132 LOAD_FAST                2 (result)
            134 STORE_SUBSCR
            138 CACHE
            140 CACHE
            142 CACHE
            144 CACHE
            146 CACHE
            148 CACHE
            150 CACHE
            152 CACHE
            154 CACHE
            156 LOAD_FAST                4 (b)
            158 UNPACK_SEQUENCE          1
            162 CALL                     1
            170 CACHE
            172 POP_TOP
            174 JUMP_BACKWARD           57 (to 62)

 21         176 LOAD_FAST                2 (result)
        >>  178 RETURN_VALUE

关注BINARY_OP和函数名就能猜出操作,当然ai也可以

贴一个:

def main():
    user_input = input("input your flag > ")

    decrypted = []

    for i in range(len(user_input)):
        b = ord(user_input[i]) - 6
        decrypted.append(b)

    # 目标密文
    fflag = [
        85, 84, 174, 227, 132, 190, 207, 142, 77, 24, 235, 236, 231, 213, 138, 153,
        60, 29, 241, 241, 237, 208, 144, 222, 115, 16, 242, 239, 231, 165, 157, 224,
        56, 104, 242, 128, 250, 211, 150, 225, 63, 29, 242, 169
    ]

    # 加密使用的 key
    key_ints = [19, 55, 192, 222, 202, 254, 186, 190]

    def encrypt(flag_bytes, key):
        result = []
        for i in range(len(flag_bytes)):
            b = flag_bytes[i] ^ key[i % len(key)]
            result.append(b)
        return result

    encrypted_flag = encrypt(fflag, key_ints)

    if decrypted < encrypted_flag:
        print("Good job! You made it!")
    else:
        print("Nah, don't give up!")

解密

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
	int a[]={85, 84, 174, 227, 132, 190, 207, 142, 77, 24, 235, 236, 231, 213, 138, 153, 60, 29, 241, 241, 237, 208, 144, 222, 115, 16, 242, 239, 231, 165, 157, 224, 56, 104, 242, 128, 250, 211, 150, 225, 63, 29, 242, 169};
	int key[]={19, 55, 192, 222, 202, 254, 186, 190};
	for(int i=0;i<44;i++){
		printf("%c",(a[i]^key[i%8])+6);
	}
}

flag:LitCTF{6d518316-5075-40ff-873a-d1e8d632e208}

easy_rc4

rc4是对称流加密

main函数是这样的

这个是魔改的rc4

在crypt函数里面最后异或了0x20,但是在这里的魔改都不影响加解密一致,所以仍然可以动调修改数据

先将密文提取出来,40 byte

78 cc 4e 13 31 f4 73 49 4f 6c 4f 73 c0 f4 35 7e ce 27 76 4d 19 60 7a ea 44 5D C0 42 81 DA 1C F6 64 72 58 D9 94 FA F8 13

为了方便,输入40个a

将v5修改为密文,16个一组修改

修改完成后在strcmp断点,看比较

复现

easy_tea

有花指令

后面tea加密里面也有两个一样的花

只改了delta

#include <stdio.h>
#include <Windows.h>
#include <stdint.h>
void decrypt(uint32_t* v, uint32_t* k){
	uint32_t delta = 0x114514;
	uint32_t v0 = v[0], v1 = v[1], sum = delta*32;
	for (int i = 0; i < 32; i++)
	{
		v1 -= ((v0<<4) + k[2]) ^ (v0 + sum) ^ ((v0>>5) + k[3]);
		v0 -= ((v1<<4) + k[0]) ^ (v1 + sum) ^ ((v1>>5) + k[1]);
		sum -= delta;
		/* code */
	}
	v[0] = v0;
	v[1] = v1;
}

int main()
{
	uint32_t Key[] =
	{
		0x11223344, 0x55667788, 0x99AABBCC, 0xDDEEFF11
	};
	uint32_t EncFlag[] = {
		0x977457FE, 0xDA3E1880,
		0xB8169108, 0x1E95285C,
		0x1FE7E6F2, 0x2BC5FC57,
		0xB28F0FA8, 0x8E0E0644,
		0x68454425, 0xC57740D9
	};
	for (int i = 0; i < 10; i+=2)
	{
		decrypt(&EncFlag[i] , Key);
	}
	
	printf("%s\n",EncFlag);
	return 0;
}

flag:LitCTF{590939df61690383a47ed1bc6ade9d51}

FeatureExtraction

在main函数里面

线性递推卷积计算

因卷积核滑动时会“越出”输入信号边界,导致输出比输入长K−1
即输入为44,kernel为10,加密后长度为44+10-1=53,就是找到的密文的长度
卷积的本质是“翻转+滑动加权求和”

#include <stdio.h>

void deconvolve(const int *C, int C_len, const int *k, int K, int *m) {
	for (int n = 0; n < C_len - K + 1; n++) {
		m[n] = C[n];
		for (int i = 1; i < K && i <= n; i++) {
			m[n] -= m[n - i] * k[i];
		}
		m[n] /= k[0];
	}
}
int main(){
	int a[]={0x1690,0x3E58,0x6FF1,0x86F0,0x9D66,0xAB30,0xCA71,0xCF29,0xE335,0xE492,0xF1FD,0xDE80,0xD0C8,0xC235,0xB9B5,0xB1CF,0x9E9F,0x9E86,0x96B4,0xA550,0xA0D3,0xA135,0x99CA,0xACC0,0xBE78,0xC196,0xBC00,0xB5C3,0xB7F0,0xB465,0xB673,0xB71F,0xBBE2,0xCB4F,0xD2AD,0xDE20,0xEC94,0xFC30,0x104B8,0xF6EE,0xEDC9,0xE385,0xD78B,0xDE19,0xC94C,0xAD14,0x7E88,0x6BB9,0x4CC6,0x3806,0x2DC9,0x2398,0x19E1};
	int k[]={0x4C,0x69,0x74,0x43,0x54,0x46,0x32,0x30,0x32,0x35};
	int m[44]={0};
	deconvolve(a,53,k,10,m);
	for(int i=0;i<44;i++){
		printf("%c",m[i]);
	}
}

flag:LitCTF{1e5a6230-308c-47cf-907c-4bfafdec8296}

Robbie Wanna Revenge

用的是那种非预期的解法

大佬文章:https://xz.aliyun.com/news/18103

https://www.cnblogs.com/x1aOha0/p/18897408

用CE去修改游戏

好像只有7.5以上能用,但是我的汉化7.5用不了,就去官网下的最新版

先后点击这两个,分别是激活和分析 .NET 程序的结构

右键单击

直接显示flag:LitCTF{Rm4ldulG05le0xaN4_LITCTF2025_Wa4jhzlZ05cm0qhF4}

常规解法

(参照了很多佬的文章)

如下:

https://blog.csdn.net/qq_24481913/article/details/148234091?spm=1001.2014.3001.5501

https://www.cnblogs.com/x1aOha0/p/18897408

https://xz.aliyun.com/news/18103

直接使用il2cppdump不能自动操作

因为GameAssembly.dll被upx加壳,其中标志位被改成了LIT,修改回去,脱壳

Il2CppDumper.exe <executable-file> <global-metadata> <output-directory>

ok,成功dump

DummyDll文件夹下面的Assembly-CSharp.dll用dnspy打开

看到decode和encode函数,但是具体逻辑无法看见

用ida打开GameAssembly.dll,恢复符号表,久等一会儿...恢复成功后就可以搜索到函数名了(我不会解密(#_<-))

但是我们可以看到PlayerHealth和playerdied,这就和游戏逻辑有关了

在PlayerHealth函数里面,看到playerdied的偏移是0x28,也就是文章里面说的“rbx偏移28其实就是判断之前的playerdied”

将判断下面的jnz改为jz,就无敌了

但是我不会玩这个游戏(┬┬﹏┬┬)

不恢复符号表

如果选择不恢复符号表,直接通过偏移量找到PlayerHealth函数就行了

在dnspy里面看到PlayerHealth只有三个方法或函数的偏移量,就可以直接在ida里面一个个尝试(复制偏移量,ida按G跳转到相应地址)

ai对此的解释:

类概述

  • 继承自 MonoBehavior,说明这是一个可以附加到游戏对象上的 Unity 组件

  • 使用 Il2CppDummyDll 属性,表明这可能来自使用 IL2CPP 编译的游戏(常见于 Unity 移动端/主机版游戏)

字段说明

  • deathVFXPrefab (GameObject):

玩家死亡时生成的预制体(可能是粒子效果等视觉特效)

内存偏移量 0x18

  • deathFogVFXPrefab (GameObject):

另一个死亡特效预制体,可能是雾效/氛围效果

内存偏移量 0x20

  • playerdied (bool):

标记玩家是否已死亡的布尔值

内存偏移量 0x28

  • trapsLayer (int):

存储陷阱层级索引的私有字段(用于碰撞检测)

内存偏移量 0x2C

方法说明

  • Start():

空方法(可能用于初始化但目前没有内容)

内存地址 0x1807A38B0

  • OnTriggerEnter2D(Collider2D collision):

当玩家的碰撞体进入另一个2D触发器碰撞体时调用的方法

目前为空但可能用于处理导致玩家死亡的陷阱碰撞

内存地址 0x1807A3560

  • PlayerHealth():

构造函数(在 Unity MonoBehaviour 脚本中较少使用)

内存地址 0x180178210

posted @ 2025-05-27 17:16  zzz222666  阅读(160)  评论(0)    收藏  举报