aliyunctf 2025 babygame bevy Engine探索与rust逆向

admin 2025年4月9日20:34:09评论5 views字数 16089阅读53分37秒阅读模式

题目背景

这道题是由bevyy引擎(rust实现)驱动的rpg游戏:

aliyunctf 2025 babygame bevy Engine探索与rust逆向

flag就是左右两边输入的数字

aliyunctf 2025 babygame bevy Engine探索与rust逆向

1

搜索源码

官方wp说可以根据特征找到源码,反正我没找到。我直接使用见闻色霸气(偷看wp)找到了:

https://github.com/PraxTube/tsumi

2

bevy 初探

得到源码,查看main.rs中最核心的部分:

fn main() {
App::new()
// 省略
.add_plugins((
world::WorldPlugin,
audio::GameAudioPlugin,
player::PlayerPlugin,
utils::UtilsPlugin,
aspect::AspectPlugin,
ui::UiPlugin,
npc::NpcPlugin,
))
.run();
}

要弄懂这道题的逻辑,必须先搞懂bevy的逻辑,不过对于这道题只需要两个机制就可以了:

1.add_system()

2.add_event()

add_system

定义

◆add_system 是 Bevy 的 App 或 Schedule 的方法,用于向引擎的更新循环(Schedule)中添加一个系统(System)

◆系统是 Bevy 的核心概念,是一段逻辑代码,负责处理游戏状态、组件、资源等,通常在每一帧运行。

作用

◆添加一个函数或闭包作为系统,使其在指定的阶段(Stage)或条件下定期执行。

◆系统可以访问和修改游戏世界的数据(如 Component、Resource)或处理事件。

用法

◆通过 App 的 add_system 方法将系统注册到默认的更新循环(Update 阶段)。

◆可以指定运行顺序或条件,例如 .before()、.after() 或 .run_if()。

use bevy::prelude::*;

fn move_player(mut query: Query<&mut Transform, With<Player>>) {
for mut transform in query.iter_mut() {
transform.translation.x += 1.0; // 每帧移动玩家
}
}

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_system(move_player) // 添加系统
.run();
}

add_event

定义

◆add_event 是 Bevy 的 App 的方法,用于注册一个自定义的**事件(Event)**类型。

◆事件是 Bevy 的事件系统中用于系统间通信的机制,允许一个系统发送信号,另一个系统监听并响应。

作用

◆定义一个事件类型(如 struct MyEvent),并将其注册到 Bevy 的事件系统中。

◆之后,可以通过 EventWriter 发送事件,通过 EventReader 读取事件。

用法

◆需要先用 add_event::<T> 注册事件类型。

◆然后在系统中使用 EventWriter<T> 发送事件,使用 EventReader<T> 监听事件。

示例:

use bevy::prelude::*;

#[derive(Debug)] // 自定义事件
struct PlayerHitEvent {
damage: f32,
}

fn detect_collision(
mut ev_hit: EventWriter<PlayerHitEvent>,
query: Query<&Transform, With<Player>>,
) {
for transform in query.iter() {
if transform.translation.x > 100.0 {
ev_hit.send(PlayerHitEvent { damage: 10.0 }); // 发送事件
}
}
}

fn handle_hit(mut ev_hit: EventReader<PlayerHitEvent>) {
for event in ev_hit.iter() {
println!("玩家受到 {} 伤害", event.damage); // 处理事件
}
}

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_event::<PlayerHitEvent>() // 注册事件
.add_system(detect_collision) // 发送事件的系统
.add_system(handle_hit) // 监听事件的系统
.run();
}

在这里的add_plugins很简单就是加一个插件的意思,这里是模块化思想。

3

梳理逻辑

拿aspect/combiner.rs 举例:

impl Plugin for AspectCombinerPlugin {
fn build(&self, app: &mut App) {
app.add_systems(
Update,
(
select_aspects,
show_combiner_icon,
select_combined_aspect,
check_all_aspects_full,
)
.run_if(in_state(GameState::Gaming)),
)
.init_resource::<Combiner>()
.add_event::<CombinedAspect>();
}
}

update指定了 select_aspects,show_combiner_icon, select_combined_aspect,check_all_aspects_full,这四个函数会在游戏的每一帧运行,条件就是run_if中,在Gaming的状态。

在源代码的仓库中,有这个游戏的网页版,玩一遍之后游戏的流程也就有数了。

游戏逻辑是:

1.选择左右两边的数字

2.触发对话(spawn_dialogue_runner)

3.对话结束,接着选择

4.选择一定次数后触发小床 (check_all_aspects_full)

5.上床之后游戏结束 (highlight_and_select_bed)

6.触发ending对话 (trigger_ending_dialogue)

其中的flag的加密,就在其中的逻辑的处理函数中,共有四个,按加密顺序为:

1.spawn_dialogue_runner

2.check_all_aspects_full

3.highlight_and_select_bed

4.trigger_ending_dialogue

逻辑一目了然,spawn_dialogue_runner会与输入的数字运算,check_all_aspects_full,highlight_and_select_bed,trigger_ending_dialogue之中有 xxtea的标志字段逻辑一目了然。

找到加密的函数具体是哪个还是比较费劲的,一个个的找,除了用xxtea的一眼顶真,spawn_dialogue_runner中的加密逻辑比较难找,但是话说回来,flag和游戏中的输入等价的话,那么找到输入的处理部分就是入口了,找到入口是一个思路。其次,在找到前三个之后,你会发现加密逻辑都用混淆,所以同样的思路,哪个函数中使用了相同的混淆,那就是了。总之,比较难找,需要细心与耐心。

4

rust 逆向与混淆

如何定位函数,逆rust啊,要有结构体的思想,有时候指针指向的位置不一定是你想要的,但是如果你往后找一下,会找到你想要的。

crtl+ F函数名的字符串,然后交叉引用,到注册的地方(bevy_ecs::schedule::config::NodeConfigs::new_system),找到这个指针然后点进去,往后找到run_safe,就是这个函数的具体实现了。

aliyunctf 2025 babygame bevy Engine探索与rust逆向

spawn_dialogue_runner

1.获取输入

.text:000000000004A33C                 mov     eax, [rdi+78h]

这里读取输入的数组,这里的rdi是从前面调用bevy_ecs::system::system_param::impl_7::get_param_alictf::aspect::combiner::Combiner_获取的。(debug的时候我每次都选33,因为离那个台子最近,我以为是常量,在这里卡了半天

1.破除混淆

接着往下看:

.text:000000000004A358                 mov     r11d, 0C57EE56Bh
.text:000000000004A35E mov eax, 0EA433459h

这里往下就是第一个混淆了。

可以发现程序使用switch 进行了控制流平坦化混淆,导致ida不能正常反编译还原正常的算法。混淆算法可以抽象为:

case 11111:
enc_part_1
case 22222:
enc_part_3
case 22222:
enc_part_2

总之就是源代码的每一行都给你插入一个switch,让ida识别不到,手动跟一下,就能知道逻辑。

这里可以选择手都debug或者静态计算每个xor的结果,总之就是找到正确的执行顺序后,收到patch就好了。

patch结果:

text:000000000004A358                 mov     r11d, 0C57EE56Bh
.text:000000000004A35E mov eax, 0EA433459h
.text:000000000004A363 xor r14d, r14d
.text:000000000004A366 xor esi, esi
.text:000000000004A368 nop
.text:000000000004A369 nop
.text:000000000004A36A nop
.text:000000000004A36B nop
.text:000000000004A36C nop
.text:000000000004A36D nop
.text:000000000004A36E nop
.text:000000000004A36F nop
.text:000000000004A370 nop
.text:000000000004A371 nop
.text:000000000004A372 nop
.text:000000000004A373 nop
.text:000000000004A374 nop
.text:000000000004A375 nop
.text:000000000004A376 nop
.text:000000000004A377 nop
.text:000000000004A378 nop
.text:000000000004A379 nop
.text:000000000004A37A nop
.text:000000000004A37B nop
.text:000000000004A37C nop
.text:000000000004A37D nop
.text:000000000004A37E nop
.text:000000000004A37F nop
.text:000000000004A380 nop
.text:000000000004A381 nop
.text:000000000004A382 nop
.text:000000000004A383 nop
.text:000000000004A384 nop
.text:000000000004A385 nop
.text:000000000004A386 nop
.text:000000000004A387 nop
.text:000000000004A388 nop
.text:000000000004A389 nop
.text:000000000004A38A nop
.text:000000000004A38B nop
.text:000000000004A38C nop
.text:000000000004A38D nop
.text:000000000004A38E nop
.text:000000000004A38F nop
.text:000000000004A390 nop
.text:000000000004A391 nop
.text:000000000004A392 nop
.text:000000000004A393 nop
.text:000000000004A394 nop
.text:000000000004A395 jmp short s_1
.text:000000000004A397 ; ---------------------------------------------------------------------------
.text:000000000004A397
.text:000000000004A397 s_4: ; CODE XREF: bevy_ecs__system__function_system__impl$7__run_unsafe_void_____bevy_ecs__system__commands__Commands_bevy_ecs__change_detection__Res_bevy_yarnspinner__project__YarnProject__bevy_ecs__change_detection__ResMut_alictf__aspect__combiner__Combiner___void_____bevy_ecs__system__commands__Commands_bevy_ecs__change_detection__Res_bevy_yarnspinner__project__YarnProject__bevy_ecs__change_detection__ResMut_alictf__aspect__combiner__Combiner___+8D5↓j
.text:000000000004A397 mov rax, rsi
.text:000000000004A39A shld rax, r14, 30h
.text:000000000004A39F mov rdx, rsi
.text:000000000004A3A2 shr rdx, 10h
.text:000000000004A3A6 xor rsi, rdx
.text:000000000004A3A9 xor r14, rax
.text:000000000004A3AC mov eax, 4AD5EDBFh
.text:000000000004A3B1 jmp short loc_4A403
.text:000000000004A3B3 ; ---------------------------------------------------------------------------
.text:000000000004A3B3
.text:000000000004A3B3 s_3: ; CODE XREF: bevy_ecs__system__function_system__impl$7__run_unsafe_void_____bevy_ecs__system__commands__Commands_bevy_ecs__change_detection__Res_bevy_yarnspinner__project__YarnProject__bevy_ecs__change_detection__ResMut_alictf__aspect__combiner__Combiner___void_____bevy_ecs__system__commands__Commands_bevy_ecs__change_detection__Res_bevy_yarnspinner__project__YarnProject__bevy_ecs__change_detection__ResMut_alictf__aspect__combiner__Combiner___+8F9↓j
.text:000000000004A3B3 mov rax, r14
.text:000000000004A3B6 mul r10
.text:000000000004A3B9 imul rsi, 19660Dh
.text:000000000004A3C0 mov r14, rax
.text:000000000004A3C3 add r14, 3C6EF35Fh
.text:000000000004A3CA adc rsi, rdx
.text:000000000004A3CD mov eax, 6DFC18B3h
.text:000000000004A3D2 jmp short s_4
.text:000000000004A3D4 ; ---------------------------------------------------------------------------
.text:000000000004A3D4
.text:000000000004A3D4 s_1: ; CODE XREF: bevy_ecs__system__function_system__impl$7__run_unsafe_void_____bevy_ecs__system__commands__Commands_bevy_ecs__change_detection__Res_bevy_yarnspinner__project__YarnProject__bevy_ecs__change_detection__ResMut_alictf__aspect__combiner__Combiner___void_____bevy_ecs__system__commands__Commands_bevy_ecs__change_detection__Res_bevy_yarnspinner__project__YarnProject__bevy_ecs__change_detection__ResMut_alictf__aspect__combiner__Combiner___+898↑j
.text:000000000004A3D4 mov eax, 3F551311h
.text:000000000004A3D9 mov r14, rcx
.text:000000000004A3DC mov rsi, r8
.text:000000000004A3DF jmp short $+2
.text:000000000004A3E1 ; ---------------------------------------------------------------------------
.text:000000000004A3E1
.text:000000000004A3E1 s_2: ; CODE XREF: bevy_ecs__system__function_system__impl$7__run_unsafe_void_____bevy_ecs__system__commands__Commands_bevy_ecs__change_detection__Res_bevy_yarnspinner__project__YarnProject__bevy_ecs__change_detection__ResMut_alictf__aspect__combiner__Combiner___void_____bevy_ecs__system__commands__Commands_bevy_ecs__change_detection__Res_bevy_yarnspinner__project__YarnProject__bevy_ecs__change_detection__ResMut_alictf__aspect__combiner__Combiner___+8E2↑j
.text:000000000004A3E1 mov rax, rsi
.text:000000000004A3E4 shld rax, r14, 30h
.text:000000000004A3E9 mov rdx, rsi
.text:000000000004A3EC shr rdx, 10h
.text:000000000004A3F0 xor rsi, rdx
.text:000000000004A3F3 xor r14, rax
.text:000000000004A3F6 jmp short s_3
.text:000000000004A3F8 ; ---------------------------------------------------------------------------
.text:000000000004A3F8 nop
.text:000000000004A3F9 nop
.text:000000000004A3FA nop
.text:000000000004A3FB
.text:000000000004A3FB loc_4A3FB:
.text:000000000004A3FB nop
.text:000000000004A3FC nop
.text:000000000004A3FD nop
.text:000000000004A3FE nop
.text:000000000004A3FF nop
.text:000000000004A400 nop
.text:000000000004A401 nop
.text:000000000004A402 nop
.text:000000000004A403
.text:000000000004A403 loc_4A403: ; CODE XREF: bevy_ecs__system__function_system__impl$7__run_unsafe_void_____bevy_ecs__system__commands__Commands_bevy_ecs__change_detection__Res_bevy_yarnspinner__project__YarnProject__bevy_ecs__change_detection__ResMut_alictf__aspect__combiner__Combiner___void_____bevy_ecs__system__commands__Commands_bevy_ecs__change_detection__Res_bevy_yarnspinner__project__YarnProject__bevy_ecs__change_detection__ResMut_alictf__aspect__combiner__Combiner___+8B4↑j
.text:000000000004A403 cmp r9d, 0Ah

其实就是手动把这些控制流恢复,修改jmp的地址就可以了。

往下,还有个同样的混淆,同样的方法patch,最后反编译的一下舒服多了。

 v44 = (0x19660D * (unsigned __int128)*(unsigned int *)(v6 + 0x78)) >> 64;
v45 = *(_DWORD *)(v6 + 0x4C); // input
v46 = 0x19660DLL * *(unsigned int *)(v6 + 0x78) + 0x3C6EF35F;
*((_QWORD *)&v49 + 1) = v44;
*(_QWORD *)&v49 = v46;
v48 = 0x19660D * (unsigned __int128)((unsigned __int64)(v49 >> 16) ^ v46)// add ADD_CONST
+ __PAIR128__(0x19660D * ((v44 >> 16) ^ v44), 0x3C6EF35FLL);
v47 = ((__int64)v48 >> 16) ^ v48;
if ( *(_DWORD *)(v6 + 0x48) < 0xAu )
v45 = *(_DWORD *)(v6 + 0x48);
v50 = HIDWORD(v72);
v51 = (_DWORD *)v72;
*(_DWORD *)v72 = HIDWORD(v72);
*(_DWORD *)(v6 + 0x78) = v47;
if ( !*(_QWORD *)(v6 + 16) )
{
v52 = std::time::SystemTime::now(v46, *((_QWORD *)&v48 + 1) >> 16, v44);
std::time::SystemTime::duration_since(&v73, v52, v53);
if ( (_BYTE)v73 )
v54 = 0LL;
else
v54 = v74;
*(_QWORD *)(v6 + 0x70) = v54;
}
*v51 = v50;
alloc::vec::Vec::push_u8_alloc::alloc::Global_(
v6,
HIBYTE(v47) + (HIWORD(v47) ^ ((v47 >> 8) + (v47 ^ v45))),
&off_1070390,
HIBYTE(v47));

不过,就算这样,ida也恢复的不准,比如v48的计算就不对:

  v48 = 0x19660D * (unsigned __int128)((unsigned __int64)(v49 >> 16) ^ v46)// add ADD_CONST
+ __PAIR128__(0x19660D * ((v44 >> 16) ^ v44), 0x3C6EF35FLL);

正确的计算应该是:

  v48 = 0x19660D * (unsigned __int128)((unsigned __int64)(v49 >> 16) ^ v46 + 0x3C6EF35FLL)// add ADD_CONST
+ __PAIR128__(0x19660D * ((v44 >> 16) ^ v44), 0x3C6EF35FLL);

然后就是  *(_DWORD *)(v6 + 0x78) = v47;作为下一轮的种子,然后与输入计算,存入vec。

最后算法总结为:

v6_120 = 0  # *(unsigned int *)(v6 + 120) last_random
v6_76 = 0x2c # *(unsigned int *)(v6 + 76) input

# 常量
MULTIPLIER = 0x19660D
ADD_CONST = 0x3C6EF35F

# 步骤 1: 计算 v44
v44 = (MULTIPLIER * v6_120) >> 64

# 步骤 2: 读取 v45
v45 = v6_76

# 步骤 3: 计算 v46
v46 = MULTIPLIER * v6_120 + ADD_CONST

# 步骤 4: 将 v44 和 v46 组合成 v49
v49 = (v44 << 64) | v46

# 步骤 5: 计算 v48
v48 = ( MULTIPLIER * ((v49 >> 16) ^ v46) + ADD_CONST +
((MULTIPLIER * ((v44 >> 16) ^ v44)) << 64 +
ADD_CONST
))

# 步骤 6: 计算 v47
v47 = (v48 >> 16) ^ v48

# 输出结果
print(f"v44 = {hex(v44)}")
print(f"v45 = {hex(v45)}")
print(f"v46 = {hex(v46)}")
print(f"v49 = {hex(v49)}")
print(f"(v49 >> 16) ^ v46 = {hex((v49 >> 16) ^ v46)}")
print(f"v48 = {hex(v48 & 2**64 -1)}")
print(f"v47 = {hex(v47 & 2 ** 32 -1)}")

v47 = v47 & 2 ** 32 -1
a = (v47 ^ v45)
b = a + (v47 >> 8)
c = b ^ (v47 >> 16)
d = c + (v47 >> 24)
print(f"a = {hex(a)}")
print(f"b = {hex(b)}")
print(f"c = {hex(c)}")
print(f"d = {hex(d)}")

res = (((v47 ^ v45) + (v47 >> 8) ) ^ (v47 >> 16)) + (v47 >> 24)
print(f"res = {hex(res)}")

check_all_aspects_full

可以看到这里4组处理数字的模式,操作的vec正是前面push的,长度是0x40,这下flag长度也知道。

if ( result >= 0x40 && !*(_QWORD *)(v8 + 40) )
{
v39 = v10;
v48 = v9;
v49 = v4;
v40 = v3;
v47 = v8 + 24;
*(_QWORD *)&v44 = 0LL;
*((_QWORD *)&v44 + 1) = 4LL;
v12 = 4LL;
v13 = 0LL;
for ( i = v8; ; v8 = i )
{
v45 = v13;
if ( v13 == 16 )
break;
v14 = v8;
v15 = *(unsigned __int8 *)alloc::vec::impl_13::index_bool_usize_alloc::alloc::Global_(
*(_QWORD *)(v8 + 8),
*(_QWORD *)(v8 + 16),
4 * v13,
&off_105A3A8);
v16 = *(unsigned __int8 *)alloc::vec::impl_13::index_bool_usize_alloc::alloc::Global_(
*(_QWORD *)(v14 + 8),
*(_QWORD *)(v14 + 16),
4 * v13 + 1,
&off_105A3C0);
v17 = *(unsigned __int8 *)alloc::vec::impl_13::index_bool_usize_alloc::alloc::Global_(
*(_QWORD *)(v14 + 8),
*(_QWORD *)(v14 + 16),
4 * v13 + 2,
&off_105A3D8);
v18 = *(unsigned __int8 *)alloc::vec::impl_13::index_bool_usize_alloc::alloc::Global_(
*(_QWORD *)(v14 + 8),
*(_QWORD *)(v14 + 16),
4 * v13 + 3,
&off_105A3F0);
if ( v13 == (_QWORD)v44 )
{
alloc::raw_vec::RawVec::grow_one_naga::arena::Handle_enum2__naga::Expression____alloc::alloc::Global_(
&v44,
&off_105A408);
v12 = *((_QWORD *)&v44 + 1);
}
z = v17 << 16;
*(_DWORD *)(v12 + 4 * v13++) = (v18 << 24) | z | v15 | (v16 << 8);
}

后面就是对xxtea的混淆了,对xxtea太熟了,懒得patch了,直接找变化。

xxtea_sum_add:                          ; CODE XREF: bevy_ecs__system__function_system__impl$7__run_unsafe_void_____bevy_ecs__change_detection__ResMut_alictf__aspect__combiner__Combiner__bevy_ecs__system__query__Query_ref$_alictf__aspect__socket__Socket__tuple$______void_____bevy_ecs__change_detection__ResMut_alictf__aspect__combiner__Combiner__bevy_ecs__system__query__Query_ref$_alictf__aspect__socket__Socket__tuple$______+281↑j
.text:0000000000044DE1 mov rcx, qword ptr [rsp+158h+sum]
.text:0000000000044DE6 add ecx, 6BC6121Dh
.text:0000000000044DEC mov qword ptr [rsp+158h+sum], rcx
.text:0000000000044DF1 mov ecx, 0E7B9CB0Dh
.text:0000000000044DF6 jmp short loc_44E0C

这里就改了DELTA,6BC6121Dh。还行,太凉心啦!

highlight_and_select_bed

同理也是xxtea加密,只改了DELTA

trigger_ending_dialogue

除了XXTEA加密外,还在加密前后加入了常量xor,很简单一眼看出来混淆前的样子。至此,这道题目的加密逻辑分析完毕。

5

解密

MULTIPLIER = 0x19660D
ADD_CONST = 0x3C6EF35F

def random(num):
v44 = (MULTIPLIER * num) >> 64
v46 = MULTIPLIER * num + ADD_CONST
v49 = (v44 << 64) | v46
v48 = ( MULTIPLIER * ((v49 >> 16) ^ v46) + ADD_CONST +
((MULTIPLIER * ((v44 >> 16) ^ v44)) << 64 +
ADD_CONST
))
v47 = ((v48 >> 16) ^ v48) & 2 **32 -1
return v47

def xor_enc(plain,num):
enc = (((num ^ plain) + (num >> 8) ) ^ (num >> 16)) + (num >> 24)
enc = enc & 0xff
return enc

def xor_dec(enc,num):
c = enc - (num >> 24)
b = c ^ (num >> 16)
a = b - (num >> 8)
plain = a ^ num
plain = plain & 0xff
return plain

def enc_start(plain):
enc = []
num = 0
for i in range(len(plain)):
num = random(num)
enc.append(xor_enc(plain[i],num))
return bytes(enc)

def dec_final(enc):
dec = []
num = 0
for i in range(len(enc)):
num = random(num)
dec.append(xor_dec(enc[i],num))
return dec

import struct

def xxtea_xor_decrypt(enc,key,delta=0x9E3779B9):
enc_flag = [struct.unpack('<I', enc[i:i+4])[0] for i in range(0, len(enc), 4)]
n = len(enc_flag)

for i in range(n):
enc_flag[i] ^= 0x71F28B88

# key = [struct.unpack('<I', key[i:i+4])[0] for i in range(0, len(key), 4)]
rounds = 6 + 52// n
sum = (delta * rounds) & ((1 << 32) -1)
y = enc_flag[0]
while rounds:
for i in range(n-1,0,-1):
z = enc_flag[i-1]
MX = ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((key[((sum >> 2) ^ i) & 3] ^ z) + (sum ^ y))
enc_flag[i] = (enc_flag[i] - MX) & ((1 << 32) -1)
y = enc_flag[i]
z = enc_flag[n-1]
MX = ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((key[((sum >> 2) ^ 0) & 3] ^ z) + (sum ^ y))
enc_flag[0] = (enc_flag[0] - MX) & ((1 << 32) -1)
y = enc_flag[0]
sum = (sum - delta) & ((1 << 32) -1)
rounds-=1

for i in range(n):
enc_flag[i] ^= 0x42E2B468

return b''.join([struct.pack('<I', num) for num in enc_flag])

def xxtea_decrypt(enc,key,delta=0x9E3779B9):
enc_flag = [struct.unpack('<I', enc[i:i+4])[0] for i in range(0, len(enc), 4)]
n = len(enc_flag)
key = [struct.unpack('<I', key[i:i+4])[0] for i in range(0, len(key), 4)]
rounds = 6 + 52// n
sum = (delta * rounds) & ((1 << 32) -1)
y = enc_flag[0]
while rounds:
for i in range(n-1,0,-1):
z = enc_flag[i-1]
MX = ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((key[((sum >> 2) ^ i) & 3] ^ z) + (sum ^ y))
enc_flag[i] = (enc_flag[i] - MX) & ((1 << 32) -1)
y = enc_flag[i]
z = enc_flag[n-1]
MX = ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((key[((sum >> 2) ^ 0) & 3] ^ z) + (sum ^ y))
enc_flag[0] = (enc_flag[0] - MX) & ((1 << 32) -1)
y = enc_flag[0]
sum = (sum - delta) & ((1 << 32) -1)
rounds-=1
return b''.join([struct.pack('<I', num) for num in enc_flag])

enc = bytes([0xA2, 0xB7, 0x9C, 0xC3, 0xB6, 0xF2, 0xB4, 0xE3, 0x2A, 0xE6,
0x96, 0x55, 0xF8, 0xD0, 0x0E, 0xAD, 0x65, 0xB0, 0xAE, 0xB3,
0x9D, 0x7E, 0x6A, 0x49, 0x46, 0x6E, 0x0E, 0x41, 0x35, 0x22,
0xF7, 0x02, 0x4F, 0x86, 0xD6, 0x11, 0xC3, 0x86, 0xA6, 0x8F,
0xDC, 0x03, 0xFE, 0x72, 0xC5, 0xE2, 0x9F, 0x0B, 0xE3, 0x4D,
0x09, 0x80, 0x95, 0xA6, 0xA2, 0xF6, 0x93, 0xD7, 0xAC, 0xA9,
0x53, 0x42, 0x61, 0x0F])

xxtea_key1 = [0x41661F49, 0xDFC12FCF, 0x1FE0F1A2, 0x71168786]
dec_1 = xxtea_xor_decrypt(enc,xxtea_key1,0x98D846DC)
enc = dec_1

xxtea_key2 = bytes([0x80, 0xE5, 0x51, 0x9E, 0x00, 0x60, 0x49, 0xF4, 0xED, 0x8E,
0x16, 0x64, 0xBF, 0x55, 0x6E, 0x49])
dec_2 = xxtea_decrypt(enc,xxtea_key2,0xB72908F9)
enc = dec_2

xxtea_key3 = bytes([0x62, 0x76, 0x65, 0xAF, 0x4B, 0x14, 0x6F, 0xFC, 0x6C, 0x2B,
0xAB, 0x22, 0xCB, 0x2D, 0x7D, 0x36])
dec_3 = xxtea_decrypt(enc,xxtea_key3,0x6BC6121D)
enc = dec_3

print(enc)
flag = dec_final(enc)
print(flag)

最后解出是一串数字,怎么变成flag呢?

aliyunctf 2025 babygame bevy Engine探索与rust逆向

奥,md5啊。害!

aliyunctf 2025 babygame bevy Engine探索与rust逆向

看雪ID:SleepAlone

https://bbs.kanxue.com/user-home-950548.htm

*本文为看雪论坛优秀文章,由 SleepAlone 原创,转载请注明来自看雪社区

原文始发于微信公众号(看雪学苑):aliyunctf 2025 babygame bevy Engine探索与rust逆向

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月9日20:34:09
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   aliyunctf 2025 babygame bevy Engine探索与rust逆向https://cn-sec.com/archives/3935869.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息