斜颚的出题人已经不满足于在re中添加go和rust的题目,在SCTF2023中更是把触手伸到了pwn题目。算是轻松地拿下ancient cgi后,直接被后续的rust pwn吓退,从此一蹶不振在pwn方向颗粒无收,流下了没有re基础的眼泪。最后被彪哥带飞到第五名。
先从最简单的print hello world程序开始分析,这里用的rust源码很简单:
1 2 3 fn main () { println! ("Hello, world!" ); }
在target/debug中生成了二进制文件,checksec一下:
1 2 3 4 5 6 7 zyd@Dori:~/ctf/world/target/debug$ checksec ./world [*] '/home/zyd/ctf/world/target/debug/world' Arch: amd64-64-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled
很有武德,除了canary默认全开,接下来直接上ghidra:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void main (int param_1,u8 **param_2) { std ::rt::lang_start<()>(world::world::main,(long )param_1,param_2,0 ); return ; } void world::world::main(void ){ &[&str] in_stack_ffffffffffffffc8; core::fmt::Arguments::new_const((Arguments *)&stack0xffffffffffffffd0,in_stack_ffffffffffffffc8); std ::io::stdio::_print(&stack0xffffffffffffffd0); return ; }
终于理解了原来上周末我逆向的是个鬼,跑到std::rt::lang_start里看什么都没找到。不过用ghidra不会把namespace中的函数归类为function而是直接放在namespaces里,上周碌碌无为在funtion里找了半天什么都没有,乐。
hello world字符串在new_const函数中被初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Arguments * core::fmt::Arguments::new_const(Arguments *__return_storage_ptr__,&[&str] pieces) { ulong in_RDX; &str *in_RSI; Arguments *__return_storage_ptr___00; Arguments local_50; undefined8 local_18; if (in_RDX < 2 ) { (__return_storage_ptr__->pieces).data_ptr = in_RSI; (__return_storage_ptr__->pieces).length = in_RDX; *(undefined8 *)&__return_storage_ptr__->fmt = 0 ; *(undefined8 *)&(__return_storage_ptr__->fmt).field_0x8 = local_18; (__return_storage_ptr__->args).data_ptr = (ArgumentV1 *)"Hello, world!\n" ; (__return_storage_ptr__->args).length = 0 ; return __return_storage_ptr__; } __return_storage_ptr___00 = &local_50; new_const(__return_storage_ptr___00,(&[&str])CONCAT88(in_RDX,__return_storage_ptr___00)); panicking::panic_fmt(__return_storage_ptr___00,&DAT_0014c308); }
接下来做一点稍微复杂的操作,看看二进制文件逆向结果又会怎么样。简简单单找了个Caesar Cipher:
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 fn encrypt (msg: &str , shift: u32 ) -> String { let alphabet_upper : &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ; let alphabet_lower : &str = "abcdefghijklmnopqrstuvwxyz" ; let mut result : String = String ::new (); for c in msg.chars () { if c.is_whitespace () { result.push (c); continue ; } if shift >= 26 { panic! ("Please specify a smaller shift." ); } if c.is_uppercase () { match alphabet_upper.chars ().position (|b| c == b) { Some (x) => { let idx : usize = shift as usize + x; let new_index = if (idx as u32 ) >= 26u32 { idx - 26usize } else { idx }; match alphabet_upper.chars ().nth (new_index) { Some (x) => { result.push (x); } None => { panic! ("No element could be found at index {}." , new_index); } }; } None => { panic! ("'{}' is not a valid element in the alphabet." , c); } }; } else { match alphabet_lower.chars ().position (|b| c == b) { Some (x) => { let idx : usize = shift as usize + x; let new_index = if (idx as u32 ) >= 26u32 { idx - 26usize } else { idx }; match alphabet_lower.chars ().nth (new_index) { Some (x) => { result.push (x); } None => { panic! ("No element could be found at index {}" , new_index); } }; } None => { panic! ("'{}' is not a valid element in the ASCII alphabet" , c); } }; } } return result; } fn decrypt (msg: &str , shift: u32 ) -> String { return encrypt (msg, 26u32 - shift); } fn main () { let msg : &str = "The quick brown fox jumped over the lazy dog" ; let shift = 2 ; let encrypted : String = encrypt (msg, shift); println! ("{}\n in a shift of {} is:\n{}" , msg, shift, encrypted); println! ("{}\n is\n{}" , encrypted, decrypt (&encrypted, shift)); }
可以看到现在的main已经惨不忍睹了😅:
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 void world::world::main(void ){ &str &Var1; undefined8 in_stack_fffffffffffffe68; undefined8 in_stack_fffffffffffffe70; undefined8 in_stack_fffffffffffffe78; undefined8 in_stack_fffffffffffffe80; &str local_108; u32 local_f4; String local_f0; Arguments local_d8; ArgumentV1 local_a8; ArgumentV1 local_98; ArgumentV1 local_88; Arguments local_78; &[core::fmt::ArgumentV1] local_48; ArgumentV1 local_38; String local_28; local_108.data_ptr = (u8 *)0x1422ed ; local_108.length = 0x2c ; local_f4 = 2 ; encrypt(&local_f0,(&str)CONCAT88(in_stack_fffffffffffffe70,in_stack_fffffffffffffe68),0x1422ed ); local_a8 = core::fmt::ArgumentV1::new_display<&str>(&local_108); local_98 = core::fmt::ArgumentV1::new_display<u32>(&local_f4); local_88 = core::fmt::ArgumentV1::new_display<alloc::string ::String>(&local_f0); core::fmt::Arguments::new_v1 (&local_d8,(&[&str])CONCAT88(in_stack_fffffffffffffe70,in_stack_fffffffffffffe68), (&[core::fmt::ArgumentV1])CONCAT88(in_stack_fffffffffffffe80,in_stack_fffffffffffffe78) ); std ::io::stdio::_print(&local_d8); local_48 = (&[core::fmt::ArgumentV1]) core::fmt::ArgumentV1::new_display<alloc::string ::String>(&local_f0); &Var1 = alloc::string ::{impl#38 }::deref(&local_f0); decrypt(&local_28,(&str)CONCAT88(in_stack_fffffffffffffe70,in_stack_fffffffffffffe68), SUB164((undefined [16 ])&Var1,0 )); local_38 = core::fmt::ArgumentV1::new_display<alloc::string ::String>(&local_28); core::fmt::Arguments::new_v1 (&local_78, (&[&str])CONCAT88(SUB168((undefined [16 ])local_38,0 ), SUB168((undefined [16 ])local_38,8 )),local_48); std ::io::stdio::_print(&local_78); core::ptr::drop_in_place<alloc::string ::String>(&local_28); core::ptr::drop_in_place<alloc::string ::String>(&local_f0); return ; }
我有点难蚌,不过更让我难蚌的是encrypt函数的逆向,今天的逆向到此为止了。