【CTF对抗-aliyunctf 2025 babygame bvey Engine探索与rust逆向】此文章归类为:CTF对抗。
赛后复现下这道题,正好也有人问这道题咋做,那就把这道题提上日程,做一做吧。
做的过程中,一头雾水,做着做着就想放弃,每次都是自己劝自己在坚持一下。最后还是自己静下心来,慢慢的抠细节,完成这道题的破解。
非常开心,特此写一篇wp记录下这道题的心路历程。顺便,截至这篇文章发布之前网上并没有这道题的详细wp,我尽量写的详细一点,看不懂官方wp的同学,可以看看这篇文章。
这道题是由bevyy引擎(rust实现)驱动的rpg游戏,
flag就是左右两边输入的数字
官方wp说可以根据特征找到源码,反正我没找到。我直接使用见闻色霸气(偷看wp)找到了:
得到源码,查看main.rs中最核心的部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | fn main() { App:: new () // 省略 .add_plugins(( world::WorldPlugin, audio::GameAudioPlugin, player::PlayerPlugin, utils::UtilsPlugin, aspect::AspectPlugin, ui::UiPlugin, npc::NpcPlugin, )) .run(); } |
要弄懂这道题的逻辑,必须先搞懂bevy的逻辑,不过对于这道题只需要两个机制就可以了:
定义
作用
用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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(); } |
定义
作用
用法
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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很简单就是加一个插件的意思,这里是模块化思想。
拿aspect/combiner.rs 举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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的状态。
在源代码的仓库中,有这个游戏的网页版,玩一遍之后游戏的流程也就有数了。
游戏逻辑是:
其中的flag的加密,就在其中的逻辑的处理函数中,共有四个,按加密顺序为:
逻辑一目了然,spawn_dialogue_runner会与输入的数字运算,check_all_aspects_full,highlight_and_select_bed,trigger_ending_dialogue之中有 xxtea的标志字段逻辑一目了然。
找到加密的函数具体是哪个还是比较费劲的,一个个的找,除了用xxtea的一眼顶真,spawn_dialogue_runner中的加密逻辑比较难找,但是话说回来,flag和游戏中的输入等价的话,那么找到输入的处理部分就是入口了,找到入口是一个思路。其次,在找到前三个之后,你会发现加密逻辑都用混淆,所以同样的思路,哪个函数中使用了相同的混淆,那就是了。总之,比较难找,需要细心与耐心。
如何定位函数,逆rust啊,要有结构体的思想,有时候指针指向的位置不一定是你想要的,但是如果你往后找一下,会找到你想要的。
crtl+ F函数名的字符串,然后交叉引用,到注册的地方(bevy_ecs::schedule::config::NodeConfigs::new_system
),找到这个指针然后点进去,往后找到run_safe,就是这个函数的具体实现了
1 | .text:000000000004A33C mov eax, [rdi+78h] |
这里读取输入的数组,这里的rdi是从前面调用bevy_ecs::system::system_param::impl_7::get_param_alictf::aspect::combiner::Combiner_获取的。(debug的时候我每次都选33,因为离那个台子最近,我以为是常量,在这里卡了半天
)
接着往下看:
1 2 | .text:000000000004A358 mov r11d, 0C57EE56Bh .text:000000000004A35E mov eax, 0EA433459h |
这里往下就是第一个混淆了。
可以发现程序使用switch 进行了控制流平坦化混淆,导致ida不能正常反编译还原正常的算法。混淆算法可以抽象为:
1 2 3 4 5 6 | case 11111: enc_part_1 case 22222: enc_part_3 case 22222: enc_part_2 |
总之就是源代码的每一行都给你插入一个switch,让ida识别不到,手动跟一下,就能知道逻辑。
这里可以选择手都debug或者静态计算每个xor的结果,总之就是找到正确的执行顺序后,收到patch就好了。
patch结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | 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,最后反编译的一下舒服多了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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的计算就不对:
1 2 | v48 = 0x19660D * (unsigned __int128)((unsigned __int64 )(v49 >> 16) ^ v46) // add ADD_CONST + __PAIR128__(0x19660D * ((v44 >> 16) ^ v44), 0x3C6EF35FLL); |
正确的计算应该是:
1 2 | v48 = 0x19660D * (unsigned __int128)((unsigned __int64 )(v49 >> 16) ^ v46 + 0x3C6EF35FLL) // add ADD_CONST + __PAIR128__(0x19660D * ((v44 >> 16) ^ v44), 0x3C6EF35FLL); |
然后就是 *(_DWORD *)(v6 + 0x78) = v47;作为下一轮的种子,然后与输入计算,存入vec。
最后算法总结为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | 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)}" ) |
可以看到这里4组处理数字的模式,操作的vec正是前面push的,长度是0x40,这下flag长度也知道。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 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了,直接找变化
1 2 3 4 5 6 | 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。还行,太凉心啦!
同理也是xxtea加密,只改了DELTA
除了XXTEA加密外,还在加密前后加入了常量xor,很简单一眼看出来混淆前的样子。
至此,这道题目的加密逻辑分析完毕。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | 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呢?
奥,md5啊。害!
更多【CTF对抗-aliyunctf 2025 babygame bvey Engine探索与rust逆向】相关视频教程:www.yxfzedu.com