688 字
3 分钟
长城杯2025初赛wp
EzFlag
这题居然是密码?难道不是逆向么?
拖到ida里面看一下main函数吧:
int __fastcall main(int argc, const char **argv, const char **envp){ __int64 v3; // rax __int64 v4; // rax char v6[32]; // [rsp+0h] [rbp-50h] BYREF int v7; // [rsp+2Ch] [rbp-24h] char v8; // [rsp+33h] [rbp-1Dh] int i; // [rsp+34h] [rbp-1Ch] unsigned __int64 v10; // [rsp+38h] [rbp-18h]
std::string::basic_string(v6, argv, envp); std::operator<<<std::char_traits<char>>(&_bss_start, "Enter password: "); std::getline<char,std::char_traits<char>,std::allocator<char>>(&std::cin, v6);
if ( (unsigned __int8)std::operator!=<char>(v6, "V3ryStr0ngp@ssw0rd") ) { v3 = std::operator<<<std::char_traits<char>>(&_bss_start, "Wrong password!"); std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>); } else { std::operator<<<std::char_traits<char>>(&_bss_start, "flag{"); std::ostream::flush((std::ostream *)&_bss_start); v10 = 1LL; for ( i = 0; i <= 31; ++i ) { v8 = f(v10); std::operator<<<std::char_traits<char>>(&_bss_start, (unsigned int)v8); std::ostream::flush((std::ostream *)&_bss_start); if ( i == 7 || i == 12 || i == 17 || i == 22 ) { std::operator<<<std::char_traits<char>>(&_bss_start, "-"); std::ostream::flush((std::ostream *)&_bss_start); } v10 *= 8LL; v10 += i + 64; v7 = 1; } v4 = std::operator<<<std::char_traits<char>>(&_bss_start, "}"); std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>); } std::string::~string(v6); return 0;}发现每进行输出flag的字符时,都会进行睡眠。
于是我一开始想把调用nanosleep的call给nop掉,结果打了好几个补丁发现还是会睡眠。
既然弄不干净那么就干脆直接逆向加密算法吧。
看看一些加密的函数:
__int64 __fastcall f(unsigned __int64 a1){ __int64 v2; // [rsp+10h] [rbp-20h] unsigned __int64 i; // [rsp+18h] [rbp-18h] __int64 v4; // [rsp+20h] [rbp-10h] __int64 v5; // [rsp+28h] [rbp-8h]
v5 = 0LL; v4 = 1LL; for ( i = 0LL; i < a1; ++i ) { v2 = v4; v4 = ((_BYTE)v5 + (_BYTE)v4) & 0xF; v5 = v2; } return *(unsigned __int8 *)std::string::operator[](&K, v5);}再寻找秘钥:
int __static_initialization_and_destruction_0(void){ char v1; // [rsp+7h] [rbp-19h] BYREF char *v2; // [rsp+8h] [rbp-18h]
v2 = &v1; std::string::basic_string(&K, "012ab9c3478d56ef", &v1); std::__new_allocator<char>::~__new_allocator(&v1); return __cxa_atexit((void (__fastcall *)(void *))&std::string::~string, &K, &_dso_handle);}发现密钥是:012ab9c3478d56ef
解题脚本
MASK64 = (1 << 64) - 1
def f_u64(a1_u64: int, k: str) -> str: n = a1_u64 % 24 v5 = 0 v4 = 1 for _ in range(n): v2 = v4 v4 = ((v5 & 0xFF) + (v4 & 0xFF)) & 0xF v5 = v2 return k[v5]
def solve(): K = "012ab9c3478d56ef" v11 = 1 out = [] for i in range(32): out.append(f_u64(v11, K)) if i in (7, 12, 17, 22): out.append("-") v11 = (v11 * 8 + i + 64) & MASK64 flag = "flag{" + "".join(out) + "}" print(flag)
if __name__ == "__main__": solve()babyGame
拖到ida瞎看一眼发现太复杂了,但是能识别出来是godot引擎开发的游戏。
于是使用GDRE进行解包,在scripts发现flag.gdc:
extends CenterContainer
@onready var flagTextEdit: Node = $PanelContainer / VBoxContainer / FlagTextEdit@onready var label2: Node = $PanelContainer / VBoxContainer / Label2
static var key = "FanAglFanAglOoO!"var data = ""
func _on_ready() -> void : Flag.hide()
func get_key() -> String: return key
func submit() -> void : data = flagTextEdit.text var aes = AESContext.new() aes.start(AESContext.MODE_ECB_ENCRYPT, key.to_utf8_buffer()) var encrypted = aes.update(data.to_utf8_buffer()) aes.finish() if encrypted.hex_encode() == "d458af702a680ae4d089ce32fc39945d": label2.show() else: label2.hide()
func back() -> void : get_tree().change_scene_to_file("res://scenes/menu.tscn")发现是AES的ECB模式加密:
- 密钥是:
FanAglFanAglOoO! - 密文是:
d458af702a680ae4d089ce32fc39945d
使用赛博厨子解密发现不对,重新读题,感觉和金币的数量有关系。
接着继续看解包文件,发现game_manager.gdc:
extends Node
@onready var fan = $"../Fan"var score = 0
func add_point(): score += 1 if score == 1: Flag.key = Flag.key.replace("A", "B") fan.visible = true发现当分数达到1时,密钥会变成 FBnFglFBnFglOoO!。
于是将 FBnFglFBnFglOoO! 作为密钥重新解密,成功得到flag。
分享
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时









